Python Pandas - jak spłaszczyć indeks hierarchiczny w kolumnach
Mam ramkę danych z hierarchicznym indeksem w osi 1 (kolumny) (z groupby."AGG"): {]}
USAF WBAN year month day s_PC s_CL s_CD s_CNT tempf
sum sum sum sum amax amin
0 702730 26451 1993 1 1 1 0 12 13 30.92 24.98
1 702730 26451 1993 1 2 0 0 13 13 32.00 24.98
2 702730 26451 1993 1 3 1 10 2 13 23.00 6.98
3 702730 26451 1993 1 4 1 0 12 13 10.04 3.92
4 702730 26451 1993 1 5 3 0 10 13 19.94 10.94
Chcę go spłaszczyć, aby wyglądał tak (nazwy nie są krytyczne - mogę zmienić nazwę):
USAF WBAN year month day s_PC s_CL s_CD s_CNT tempf_amax tmpf_amin
0 702730 26451 1993 1 1 1 0 12 13 30.92 24.98
1 702730 26451 1993 1 2 0 0 13 13 32.00 24.98
2 702730 26451 1993 1 3 1 10 2 13 23.00 6.98
3 702730 26451 1993 1 4 1 0 12 13 10.04 3.92
4 702730 26451 1993 1 5 3 0 10 13 19.94 10.94
Jak mam to zrobić? (Próbowałem wiele, bezskutecznie.)
Zgodnie z sugestią, oto głowa w formie dict
{('USAF', ''): {0: '702730',
1: '702730',
2: '702730',
3: '702730',
4: '702730'},
('WBAN', ''): {0: '26451', 1: '26451', 2: '26451', 3: '26451', 4: '26451'},
('day', ''): {0: 1, 1: 2, 2: 3, 3: 4, 4: 5},
('month', ''): {0: 1, 1: 1, 2: 1, 3: 1, 4: 1},
('s_CD', 'sum'): {0: 12.0, 1: 13.0, 2: 2.0, 3: 12.0, 4: 10.0},
('s_CL', 'sum'): {0: 0.0, 1: 0.0, 2: 10.0, 3: 0.0, 4: 0.0},
('s_CNT', 'sum'): {0: 13.0, 1: 13.0, 2: 13.0, 3: 13.0, 4: 13.0},
('s_PC', 'sum'): {0: 1.0, 1: 0.0, 2: 1.0, 3: 1.0, 4: 3.0},
('tempf', 'amax'): {0: 30.920000000000002,
1: 32.0,
2: 23.0,
3: 10.039999999999999,
4: 19.939999999999998},
('tempf', 'amin'): {0: 24.98,
1: 24.98,
2: 6.9799999999999969,
3: 3.9199999999999982,
4: 10.940000000000001},
('year', ''): {0: 1993, 1: 1993, 2: 1993, 3: 1993, 4: 1993}}
12 answers
Myślę, że najprostszym sposobem, aby to zrobić, byłoby ustawienie kolumn na najwyższym poziomie:
df.columns = df.columns.get_level_values(0)
Uwaga: Jeśli poziom to ma nazwę, Możesz również uzyskać do niego dostęp poprzez to, a nie 0.
.
Jeśli chcesz połączyć/join
Twój MultiIndex w jednym indeksie (zakładając, że masz tylko wpisy w kolumnach) możesz:
df.columns = [' '.join(col).strip() for col in df.columns.values]
Uwaga: musimy strip
Biała SPACJA, gdy nie ma drugiego indeksu.
In [11]: [' '.join(col).strip() for col in df.columns.values]
Out[11]:
['USAF',
'WBAN',
'day',
'month',
's_CD sum',
's_CL sum',
's_CNT sum',
's_PC sum',
'tempf amax',
'tempf amin',
'year']
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-17 19:36:17
pd.DataFrame(df.to_records()) # multiindex become columns and new index is integers only
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-14 08:00:21
ODPOWIEDŹ Andy ' ego Haydena jest z pewnością najprostszym sposobem-jeśli chcesz uniknąć powielania etykiet kolumn, musisz trochę poprawić
In [34]: df
Out[34]:
USAF WBAN day month s_CD s_CL s_CNT s_PC tempf year
sum sum sum sum amax amin
0 702730 26451 1 1 12 0 13 1 30.92 24.98 1993
1 702730 26451 2 1 13 0 13 0 32.00 24.98 1993
2 702730 26451 3 1 2 10 13 1 23.00 6.98 1993
3 702730 26451 4 1 12 0 13 1 10.04 3.92 1993
4 702730 26451 5 1 10 0 13 3 19.94 10.94 1993
In [35]: mi = df.columns
In [36]: mi
Out[36]:
MultiIndex
[(USAF, ), (WBAN, ), (day, ), (month, ), (s_CD, sum), (s_CL, sum), (s_CNT, sum), (s_PC, sum), (tempf, amax), (tempf, amin), (year, )]
In [37]: mi.tolist()
Out[37]:
[('USAF', ''),
('WBAN', ''),
('day', ''),
('month', ''),
('s_CD', 'sum'),
('s_CL', 'sum'),
('s_CNT', 'sum'),
('s_PC', 'sum'),
('tempf', 'amax'),
('tempf', 'amin'),
('year', '')]
In [38]: ind = pd.Index([e[0] + e[1] for e in mi.tolist()])
In [39]: ind
Out[39]: Index([USAF, WBAN, day, month, s_CDsum, s_CLsum, s_CNTsum, s_PCsum, tempfamax, tempfamin, year], dtype=object)
In [40]: df.columns = ind
In [46]: df
Out[46]:
USAF WBAN day month s_CDsum s_CLsum s_CNTsum s_PCsum tempfamax tempfamin \
0 702730 26451 1 1 12 0 13 1 30.92 24.98
1 702730 26451 2 1 13 0 13 0 32.00 24.98
2 702730 26451 3 1 2 10 13 1 23.00 6.98
3 702730 26451 4 1 12 0 13 1 10.04 3.92
4 702730 26451 5 1 10 0 13 3 19.94 10.94
year
0 1993
1 1993
2 1993
3 1993
4 1993
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 18:54:14
df.columns = ['_'.join(tup).rstrip('_') for tup in df.columns.values]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 00:25:38
I jeśli chcesz zachować informacje o agregacji z drugiego poziomu multiindex, możesz spróbować tego:
In [1]: new_cols = [''.join(t) for t in df.columns]
Out[1]:
['USAF',
'WBAN',
'day',
'month',
's_CDsum',
's_CLsum',
's_CNTsum',
's_PCsum',
'tempfamax',
'tempfamin',
'year']
In [2]: df.columns = new_cols
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 16:19:27
Po przeczytaniu wszystkich odpowiedzi, wpadłem na to:
def __my_flatten_cols(self, how="_".join, reset_index=True):
how = (lambda iter: list(iter)[-1]) if how == "last" else how
self.columns = [how(filter(None, map(str, levels))) for levels in self.columns.values] \
if isinstance(self.columns, pd.MultiIndex) else self.columns
return self.reset_index() if reset_index else self
pd.DataFrame.my_flatten_cols = __my_flatten_cols
Użycie:
Podano ramkę danych:
df = pd.DataFrame({"grouper": ["x","x","y","y"], "val1": [0,2,4,6], 2: [1,3,5,7]}, columns=["grouper", "val1", 2])
grouper val1 2
0 x 0 1
1 x 2 3
2 y 4 5
3 y 6 7
-
Metoda pojedynczej agregacji : zmienne wynikowe nazwane tak samo jak źródło:
df.groupby(by="grouper").agg("min").my_flatten_cols()
- tak samo jak
df.groupby(by="grouper",
as_index = False)
lub.agg(...)
.reset_index () ----- before ----- val1 2 grouper ------ after ----- grouper val1 2 0 x 0 1 1 y 4 5
- tak samo jak
-
Jedna zmienna źródłowa, wiele agregacji : wynikająca zmienne nazwane statystykami :
df.groupby(by="grouper").agg({"val1": [min,max]}).my_flatten_cols("last")
- tak samo jak
a = df.groupby(..).agg(..); a.columns = a.columns.droplevel(0); a.reset_index()
. ----- before ----- val1 min max grouper ------ after ----- grouper min max 0 x 0 2 1 y 4 6
- tak samo jak
-
Wiele zmiennych, wiele agregacji: zmienne wynikowe nazwane (varname)_(statname):
df.groupby(by="grouper").agg({"val1": min, 2:[sum, "size"]}).my_flatten_cols() # you can combine the names in other ways too, e.g. use a different delimiter: #df.groupby(by="grouper").agg({"val1": min, 2:[sum, "size"]}).my_flatten_cols(" ".join)
- Działa
a.columns = ["_".join(filter(None, map(str, levels))) for levels in a.columns.values]
pod maską (ponieważ ta formaagg()
dajeMultiIndex
na kolumnach). - jeśli nie masz
my_flatten_cols
helpera, może być łatwiej wpisać rozwiązanie zasugerowane przez @Seigi : W tym przypadku nie jest to jednak możliwe, jeśli na kolumnach znajdują się numeryczne etykiety.]} - do obsługi numerycznych etykiet na kolumnach, możesz użyć rozwiązania zaproponowanego przez @jxstanford i @ Nolan Conaway (
a.columns = ["_".join(tuple(map(str, t))).rstrip("_") for t in a.columns.values]
), ale nie rozumiem, dlaczego wywołanietuple()
jest potrzebne i wierzę, że {[21] } jest wymagane tylko wtedy, gdy niektóre kolumny mają deskryptor, taki jak("colname", "")
(co może się zdarzyć, jeślireset_index()
zanim spróbujesz naprawić.columns
) ----- before ----- val1 2 min sum size grouper ------ after ----- grouper val1_min 2_sum 2_size 0 x 0 4 2 1 y 4 12 2
- Działa
-
Chcesz ręcznie nazwać zmienne wynikowe: (jest to przestarzałe od pandas 0.20.0 Z brak odpowiedniej alternatywy od 0.23)
df.groupby(by="grouper").agg({"val1": {"sum_of_val1": "sum", "count_of_val1": "count"}, 2: {"sum_of_2": "sum", "count_of_2": "count"}}).my_flatten_cols("last")
-
Inne sugestie obejmują : ręczne ustawianie kolumn:
res.columns = ['A_sum', 'B_sum', 'count']
lub.join()
ING wielegroupby
poleceń. ----- before ----- val1 2 count_of_val1 sum_of_val1 count_of_2 sum_of_2 grouper ------ after ----- grouper count_of_val1 sum_of_val1 count_of_2 sum_of_2 0 x 2 2 2 4 1 y 2 10 2 12
-
Inne sugestie obejmują : ręczne ustawianie kolumn:
Sprawy obsługiwane przez pomocnika function
- nazwy poziomów mogą być nie-łańcuchowe, np. indeksowe ramki danych według numerów kolumn, gdy nazwy kolumn są liczbami całkowitymi , więc musimy przekonwertować za pomocą
map(str, ..)
Mogą być również puste, więc musimy]}
- dla kolumn jednopoziomowych (tj. cokolwiek poza MultiIndex),
columns.values
zwraca nazwy (str
, nie krotki) - w zależności od sposobu użycia
.agg()
może być konieczne zachowanie dolnej większości etykiet dla kolumny lub połączenie wielu etykiety - (odkąd jestem nowy na pandach? z tego powodu nie jest to możliwe, ponieważ nie jest to możliwe, ponieważ nie jest to możliwe.]}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-26 16:58:46
Jeśli chcesz mieć separator w nazwie między poziomami, ta funkcja działa dobrze.
def flattenHierarchicalCol(col,sep = '_'):
if not type(col) is tuple:
return col
else:
new_col = ''
for leveli,level in enumerate(col):
if not level == '':
if not leveli == 0:
new_col += sep
new_col += level
return new_col
df.columns = df.columns.map(flattenHierarchicalCol)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-03 18:12:37
Może trochę za późno, ale jeśli nie martwisz się duplikatami nazw kolumn:
df.columns = df.columns.tolist()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-30 12:29:41
Ogólne rozwiązanie, które obsługuje wiele poziomów i typów mieszanych:
df.columns = ['_'.join(tuple(map(str, t))) for t in df.columns.values]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-20 12:23:44
Podążając za @jxstanford i @tvt173, napisałem szybką funkcję, która powinna załatwić sprawę, niezależnie od nazw kolumn string/int:
def flatten_cols(df):
df.columns = [
'_'.join(tuple(map(str, t))).rstrip('_')
for t in df.columns.values
]
return df
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-10 20:35:18
Można również zrobić jak poniżej. Rozważ df
jako ramkę danych i przyjmij dwupoziomowy indeks (jak to ma miejsce w twoim przykładzie)
df.columns = [(df.columns[i][0])+'_'+(datadf_pos4.columns[i][1]) for i in range(len(df.columns))]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-28 00:30:12
Najbardziej pythoniczny sposób, aby to zrobić, aby użyć funkcji map
.
df.columns = df.columns.map(' '.join).str.strip()
Wyjście print(df.columns)
:
Index(['USAF', 'WBAN', 'day', 'month', 's_CD sum', 's_CL sum', 's_CNT sum',
's_PC sum', 'tempf amax', 'tempf amin', 'year'],
dtype='object')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 21:23:13