Python: Pandas: melt na kolumnie z multi-indeksem

2-lip-2024

Załóżmy, że dotarły do nas dane w takiej oto postaci:

Mamy więc „sztuczny” indeks wierszy 'MF’ o wartościach 0,1,2,3,4 oraz kolumny, które są opisane muli-indeksem.

  • piersza kolumna to („Age”,), bo jakakolwiek nazwa pojawia się tylko w zerowym poziomie nagłówka.
  • druga kolumna to („HalfSeconds”,”F”),
  • trzecia to („HalfSeconds”,”M”),
  • czwarta to („TotalSeconds”,”F”)
  • i ostatnia to („TotalSeconds”,”M”).

Chcielibyśmy taką pivot-table czyli tabelę przestawną „odpivotować” i zobaczyć ją w tzw. długim formacie:

Jeśli chcesz samodzilnie sobie takie dane wyczarować to użyj poniższego kodu:

import pandas as pd
import numpy as np
from datetime import datetime
from datetime import timedelta
import time

df = pd.read_csv('./marathon_results_2016.csv',
index_col='Bib',
usecols=['Bib','40K','Half','Pace','Age','M/F',
'Country','State','City'])

df['40K'] = df['40K'].apply(pd.to_timedelta, errors='coerce')
df['Half'] = df['Half'].apply(pd.to_timedelta, errors='coerce')
df = df[df['40K'].notna() & df['Half'].notna()]
df['TotalSeconds'] = df['40K'].apply(lambda x: timedelta.total_seconds(x))
df['HalfSeconds'] = df['Half'].apply(lambda x: timedelta.total_seconds(x))

df = df.pivot_table(index="Age",columns="M/F",values="TotalSeconds")

df.reset_index(inplace=True)
df.head()

Plik zdanymi można pobrać stąd

W wersji Pandasa 2.2.0 dało się bez problemu uruchomić polecenie:

df.melt(id_vars="Age", value_name='Time')

Niestety, coś tam chłopaki namieszali: https://github.com/pandas-dev/pandas/issues/57663 i komenda kończy się błędem:

KeyError: „The following id_vars or value_vars are not present in the DataFrame: [’Age’]”


Wprawdzie w wersji 2.2.2 problem miał być rzekomo usunięty, ale u mnie nadal występuje. Ponieważ ostatnie czego bym chciał, to uzależniać działanie od tego w jakiej wersji bibliotek ten program jest uruchamiany, a może jeszcze nakłaniać do korzystania ze starych bibliotek, bo na nowych coś nie działa, to proponuję przekształcenie danych. Zobacz jakie:

Błąd KeyError poniekad ma rację. W kolumnie widocznej jako Age, kluczem jest („Age”,) i jakby nie patrzeć nie jest to po prostu napis „Age”. Może wobec tego by tak przed „odpiwotowaniem” spłaszczyć ten multiindex? No właśnie, da się to zrobić!

Jest sobie sprytna metoda to_flat_index, powoduje, że MultiIndex zostanie zamieniony na Index. Pozycjami nadal są tuple, ale przynajmniej pozbyliśmy się tego „Multi”:


Ale to jest ciągle coś dziwnego! Chcielibyśmy w kolumnach widzieć po prostu napisy. Da się to zrobić!

W następnym kroku można użyć str i wyciągnąć z tego obiektu wartości zrzutowane na string i połączyć je ze sobą pustym napisem. Oto, co dostaniemy patrząc tylko na wynik:

O rety! Mamy po prostu napisy! Tak, to jest to, czego nam było trzeba. Dlatego do osiągnięcia pożądanej formy „odpivotowanej” tabeli można teraz użyć kodu:

df.columns = df.columns.to_flat_index().str.join(”)
df.melt(id_vars=”Age”, value_name=’Time’).head()

No i proszę – trochę na piechotę, ale przynajmniej bez dodawania w instrukcji informacji „upewnij się, że pracujesz ze starą wersją Pandasa”.

Komentarze są wyłączone

Autor: Rafał Kraik