Nazywanie zwracanych kolumn w funkcji agregatowej? [duplikat]

to pytanie ma już odpowiedzi tutaj : Wiele agregacji tej samej kolumny za pomocą pandy GroupBy.agg () (3 odpowiedzi) Zamknięty 1 rok temu . Mam problem z funkcjonalnością grupową pand. Przeczytałem dokumentację , ale nie wiem, jak zastosować funkcje agregujące do wielu kolumn i mają niestandardowe nazwy dla tych kolumn.

Jest to bardzo zbliżone, ale zwracana struktura danych ma zagnieżdżone nagłówki kolumn:

data.groupby("Country").agg(
        {"column1": {"foo": sum()}, "column2": {"mean": np.mean, "std": np.std}})

(tj. Chcę wziąć mean I std column2, ale zwróć te kolumny jako "mean" I "std")

Co mi umyka?

Author: David Chouinard, 2013-09-29

6 answers

Spowoduje to spadek zewnętrznego poziomu z indeksu hierarchicznej kolumny:

df = data.groupby(...).agg(...)
df.columns = df.columns.droplevel(0)

Jeśli chcesz zachować najbardziej zewnętrzny poziom, możesz użyć funkcji ravel () na wielopoziomowej kolumnie, aby utworzyć nowe etykiety:

df.columns = ["_".join(x) for x in df.columns.ravel()]

Na przykład:

import pandas as pd
import pandas.rpy.common as com
import numpy as np

data = com.load_data('Loblolly')
print(data.head())
#     height  age Seed
# 1     4.51    3  301
# 15   10.89    5  301
# 29   28.72   10  301
# 43   41.74   15  301
# 57   52.70   20  301

df = data.groupby('Seed').agg(
    {'age':['sum'],
     'height':['mean', 'std']})
print(df.head())
#       age     height           
#       sum        std       mean
# Seed                           
# 301    78  22.638417  33.246667
# 303    78  23.499706  34.106667
# 305    78  23.927090  35.115000
# 307    78  22.222266  31.328333
# 309    78  23.132574  33.781667

df.columns = df.columns.droplevel(0)
print(df.head())

      sum        std       mean
Seed                           
301    78  22.638417  33.246667
303    78  23.499706  34.106667
305    78  23.927090  35.115000
307    78  22.222266  31.328333
309    78  23.132574  33.781667

Alternatywnie, aby zachować pierwszy poziom indeksu:

df = data.groupby('Seed').agg(
    {'age':['sum'],
     'height':['mean', 'std']})
df.columns = ["_".join(x) for x in df.columns.ravel()]

      age_sum   height_std  height_mean
Seed                           
301        78    22.638417    33.246667
303        78    23.499706    34.106667
305        78    23.927090    35.115000
307        78    22.222266    31.328333
309        78    23.132574    33.781667
 120
Author: unutbu,
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-10-30 08:56:23

Dla pand >= 0,25

Funkcja nazw zwracanych kolumn zbiorczych została ponownie wprowadzona w gałęzi master i jest przeznaczona dla pand 0.25. Nowa składnia to .agg(new_col_name=('col_name', 'agg_func'). Szczegółowy przykład z PR linkowanego powyżej:

In [2]: df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
   ...:                    'height': [9.1, 6.0, 9.5, 34.0],
   ...:                    'weight': [7.9, 7.5, 9.9, 198.0]})
   ...:

In [3]: df
Out[3]:
  kind  height  weight
0  cat     9.1     7.9
1  dog     6.0     7.5
2  cat     9.5     9.9
3  dog    34.0   198.0

In [4]: df.groupby('kind').agg(min_height=('height', 'min'), 
                               max_weight=('weight', 'max'))
Out[4]:
      min_height  max_weight
kind
cat          9.1         9.9
dog          6.0       198.0

Będzie również możliwe użycie wielu wyrażeń lambda z tą składnią i dwuetapową zmianą nazwy składni, którą zaproponowałem wcześniej (poniżej) zgodnie z this PR. Ponownie kopiowanie z przykładu w PR:

In [2]: df = pd.DataFrame({"A": ['a', 'a'], 'B': [1, 2], 'C': [3, 4]})

In [3]: df.groupby("A").agg({'B': [lambda x: 0, lambda x: 1]})
Out[3]:
         B
  <lambda> <lambda 1>
A
a        0          1

A następnie .rename(), lub za jednym razem:

In [4]: df.groupby("A").agg(b=('B', lambda x: 0), c=('B', lambda x: 1))
Out[4]:
   b  c
A
a  0  0

Dla pand

Obecnie akceptowana odpowiedź przez unutbu opisuje to świetny sposób na zrobienie tego w wersjach pand

Seria:

FutureWarning: używanie dict na Serii do agregacji jest przestarzałe i zostanie usunięte w future version

DataFrames:

FutureWarning: używanie dict ze zmianą nazwy jest przestarzałe i zostanie usunięte w przyszłej wersji

Zgodnie z pandas 0.20 changelog , zalecany sposób zmiany nazw kolumn podczas agregowania jest następujący.

# Create a sample data frame
df = pd.DataFrame({'A': [1, 1, 1, 2, 2],
                   'B': range(5),
                   'C': range(5)})

# ==== SINGLE COLUMN (SERIES) ====
# Syntax soon to be deprecated
df.groupby('A').B.agg({'foo': 'count'})
# Recommended replacement syntax
df.groupby('A').B.agg(['count']).rename(columns={'count': 'foo'})

# ==== MULTI COLUMN ====
# Syntax soon to be deprecated
df.groupby('A').agg({'B': {'foo': 'sum'}, 'C': {'bar': 'min'}})
# Recommended replacement syntax
df.groupby('A').agg({'B': 'sum', 'C': 'min'}).rename(columns={'B': 'foo', 'C': 'bar'})
# As the recommended syntax is more verbose, parentheses can
# be used to introduce line breaks and increase readability
(df.groupby('A')
    .agg({'B': 'sum', 'C': 'min'})
    .rename(columns={'B': 'foo', 'C': 'bar'})
)

Zobacz 0.20 changelog aby uzyskać dodatkowe informacje.

[53]}Aktualizacja 2017-01-03 w odpowiedzi na komentarz @JunkMechanic.

W starym stylu składnia słownika, możliwe było przekazanie wielu funkcji {[10] } do .agg, ponieważ nazwy te byłyby zmieniane za pomocą klucza w przekazywanym słowniku:

>>> df.groupby('A').agg({'B': {'min': lambda x: x.min(), 'max': lambda x: x.max()}})

    B    
  max min
A        
1   2   0
2   4   3
Wiele funkcji może być również przekazywanych do jednej kolumny jako lista:
>>> df.groupby('A').agg({'B': [np.min, np.max]})

     B     
  amin amax
A          
1    0    2
2    3    4
Nie działa to jednak z funkcjami lambda, ponieważ są one anonimowe i wszystkie zwracają <lambda>, co powoduje kolizję nazw: {]}
>>> df.groupby('A').agg({'B': [lambda x: x.min(), lambda x: x.max]})
SpecificationError: Function names must be unique, found multiple named <lambda>

Aby uniknąć SpecificationError, nazwane funkcje mogą być zdefiniowane a priori zamiast lambda. Odpowiednie nazwy funkcji również unikają wywoływania .rename w ramce danych. Funkcje te mogą być przekazywane z tą samą składnią listy jak powyżej:

>>> def my_min(x):
>>>     return x.min()

>>> def my_max(x):
>>>     return x.max()

>>> df.groupby('A').agg({'B': [my_min, my_max]})

       B       
  my_min my_max
A              
1      0      2
2      3      4
 182
Author: joelostblom,
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
2019-06-04 23:02:16

Zgadzam się z OP, że bardziej naturalne i spójne wydaje się nazywanie i definiowanie kolumn wyjściowych w tym samym miejscu( np. jak to jest zrobione z tidyverse summarize W R ), ale obejściem w pandach na razie jest tworzenie nowych kolumn z żądanymi nazwami poprzez assign przed wykonaniem agregacji:

data.assign(
    f=data['column1'],
    mean=data['column2'],
    std=data['column2']
).groupby('Country').agg(dict(f=sum, mean=np.mean, std=np.std)).reset_index()

(za pomocą reset_index zakręty 'Country', 'f', 'mean', i 'std' wszystkie w regularne kolumny z oddzielnym indeksem całkowitym.)

 10
Author: teichert,
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-06-27 14:54:26

Jeśli chcesz mieć zachowanie podobne do JMP, Utwórz tytuły kolumn, które przechowują wszystkie informacje z wielu indeksów, których możesz użyć:

newidx = []
for (n1,n2) in df.columns.ravel():
    newidx.append("%s-%s" % (n1,n2))
df.columns=newidx

Zmieni ramkę danych z:

    I                       V
    mean        std         first
V
4200.0  25.499536   31.557133   4200.0
4300.0  25.605662   31.678046   4300.0
4400.0  26.679005   32.919996   4400.0
4500.0  26.786458   32.811633   4500.0

Do

    I-mean      I-std       V-first
V
4200.0  25.499536   31.557133   4200.0
4300.0  25.605662   31.678046   4300.0
4400.0  26.679005   32.919996   4400.0
4500.0  26.786458   32.811633   4500.0
 6
Author: Gadi Oron,
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-20 12:29:02

Takie jak ten rodzaj ramki danych, istnieją dwa poziomy nazwy kolumny:

 shop_id  item_id   date_block_num item_cnt_day       
                                  target              
0   0       30          1            31               

Możemy użyć tego kodu:

df.columns = [col[0] if col[-1]=='' else col[-1] for col in df.columns.values]

Wynik to:

 shop_id  item_id   date_block_num target              
0   0       30          1            31 
 1
Author: saneryee,
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
2019-03-05 10:53:13

Z inspiracji @ Joel Ostblom

Dla tych, którzy mają już wykonalny słownik służący jedynie do agregacji, możesz użyć / zmodyfikować poniższy kod dla agregacji nowszej wersji, oddzielając agregację i zmieniając nazwę części. Należy pamiętać o zagnieżdżonym słowniku, jeśli jest więcej niż 1 pozycja.

def agg_translate_agg_rename(input_agg_dict):
    agg_dict = {}
    rename_dict = {}
    for k, v in input_agg_dict.items():
        if len(v) == 1:
            agg_dict[k] = list(v.values())[0]
            rename_dict[k] = list(v.keys())[0]
        else:
            updated_index = 1
            for nested_dict_k, nested_dict_v in v.items():
                modified_key = k + "_" + str(updated_index)
                agg_dict[modified_key] = nested_dict_v
                rename_dict[modified_key] = nested_dict_k
                updated_index += 1
    return agg_dict, rename_dict

one_dict = {"column1": {"foo": 'sum'}, "column2": {"mean": 'mean', "std": 'std'}}
agg, rename = agg_translator_aa(one_dict)

Otrzymujemy

agg = {'column1': 'sum', 'column2_1': 'mean', 'column2_2': 'std'}
rename = {'column1': 'foo', 'column2_1': 'mean', 'column2_2': 'std'}

Proszę dać mi znać, jeśli jest mądrzejszy sposób, aby to zrobić. Dzięki.

 0
Author: udothemath,
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-10-08 01:26:51