Python Jak przypisać wyniki operacji groupby z powrotem do kolumn w macierzystej ramce danych?

Mam następującą ramkę danych w Ipythonie, gdzie każdy wiersz jest pojedynczym zapasem:

In [261]: bdata
Out[261]:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 21210 entries, 0 to 21209
Data columns:
BloombergTicker      21206  non-null values
Company              21210  non-null values
Country              21210  non-null values
MarketCap            21210  non-null values
PriceReturn          21210  non-null values
SEDOL                21210  non-null values
yearmonth            21210  non-null values
dtypes: float64(2), int64(1), object(4)

Chcę zastosować operację groupby, która oblicza średni zwrot ważony pułapem we wszystkim, na każdą datę w kolumnie "yearmonth".

To działa zgodnie z oczekiwaniami:

In [262]: bdata.groupby("yearmonth").apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())
Out[262]:
yearmonth
201204      -0.109444
201205      -0.290546

Ale potem chcę "nadawać" te wartości z powrotem do indeksów w oryginalnej ramce danych i zapisać je jako stałe kolumny, w których daty pasują.

In [263]: dateGrps = bdata.groupby("yearmonth")

In [264]: dateGrps["MarketReturn"] = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/mnt/bos-devrnd04/usr6/home/espears/ws/Research/Projects/python-util/src/util/<ipython-input-264-4a68c8782426> in <module>()
----> 1 dateGrps["MarketReturn"] = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())

TypeError: 'DataFrameGroupBy' object does not support item assignment

Zdaję sobie sprawę, że to naiwne zadanie nie powinno działać. Ale jaki jest idiom" właściwy " dla przypisania wyniku operacji groupby do nowej kolumny na macierzystej ramce danych?

Na koniec chcę, aby kolumna o nazwie "MarketReturn" była powtarzalną stałą wartością dla wszystkich indeksów, które mają datę dopasowania do wyniku operacji groupby.

Jeden hack, aby to osiągnąć, byłby następujący:

marketRetsByDate  = dateGrps.apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())

bdata["MarketReturn"] = np.repeat(np.NaN, len(bdata))

for elem in marketRetsByDate.index.values:
    bdata["MarketReturn"][bdata["yearmonth"]==elem] = marketRetsByDate.ix[elem]
Ale to jest powolne, złe i niepytoniczne.
Author: ely, 2012-08-30

5 answers

In [97]: df = pandas.DataFrame({'month': np.random.randint(0,11, 100), 'A': np.random.randn(100), 'B': np.random.randn(100)})

In [98]: df.join(df.groupby('month')['A'].sum(), on='month', rsuffix='_r')
Out[98]:
           A         B  month       A_r
0  -0.040710  0.182269      0 -0.331816
1  -0.004867  0.642243      1  2.448232
2  -0.162191  0.442338      4  2.045909
3  -0.979875  1.367018      5 -2.736399
4  -1.126198  0.338946      5 -2.736399
5  -0.992209 -1.343258      1  2.448232
6  -1.450310  0.021290      0 -0.331816
7  -0.675345 -1.359915      9  2.722156
 41
Author: Wouter Overmeire,
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
2012-09-08 20:26:16

Podczas gdy wciąż zgłębiam wszystkie niesamowicie inteligentne sposoby, które apply łączą podane elementy, oto inny sposób na dodanie nowej kolumny w rodzicu po operacji groupby.

In [236]: df
Out[236]: 
  yearmonth    return
0    201202  0.922132
1    201202  0.220270
2    201202  0.228856
3    201203  0.277170
4    201203  0.747347

In [237]: def add_mkt_return(grp):
   .....:     grp['mkt_return'] = grp['return'].sum()
   .....:     return grp
   .....: 

In [238]: df.groupby('yearmonth').apply(add_mkt_return)
Out[238]: 
  yearmonth    return  mkt_return
0    201202  0.922132    1.371258
1    201202  0.220270    1.371258
2    201202  0.228856    1.371258
3    201203  0.277170    1.024516
4    201203  0.747347    1.024516
 30
Author: Garrett,
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
2012-09-08 17:33:00

Czy mogę zaproponować metodę transform (zamiast agregatu)? Jeśli użyjesz go w oryginalnym przykładzie, powinien robić to ,co chcesz(nadawanie).

 15
Author: Wes McKinney,
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
2012-09-08 22:35:52

Jako ogólna zasada podczas używania groupby (), jeśli używaszfunkcja transform() zwróci tabelę o tej samej długości co oryginał. Podczas korzystania z innych funkcji, takich jak .sum () lub .first () następnie zwraca tabelę, w której każdy wiersz jest grupą.

Nie jestem pewien, jak to działa z apply, ale implementacja skomplikowanych funkcji lambda za pomocą transform może być dość trudna, więc najbardziej pomocna jest strategia tworzenia zmiennych, których potrzebuję, umieszczania ich w oryginale Zbierz Dane i wykonaj tam moje operacje.

Jeśli dobrze rozumiem, co próbujesz zrobić (przepraszam, jeśli się mylę), najpierw możesz obliczyć całkowity pułap rynkowy dla każdej grupy:

bdata['group_MarketCap'] = bdata.groupby('yearmonth')['MarketCap'].transform('sum')

Spowoduje to dodanie kolumny o nazwie "group_MarketCap" do oryginalnych danych, która zawierałaby sumę pułapów rynkowych dla każdej grupy. Następnie można obliczyć wartości ważone bezpośrednio:

bdata['weighted_P'] = bdata['PriceReturn'] * (bdata['MarketCap']/bdata['group_MarketCap'])

I na koniec obliczyłbyś średnią ważoną dla każdej grupy używając tego samego funkcja przekształcenia:

bdata['MarketReturn'] = bdata.groupby('yearmonth')['weighted_P'].transform('sum')

Mam tendencję do budowania moich zmiennych w ten sposób. Czasami można zrobić to wszystko jednym poleceniem, ale nie zawsze działa to z groupby (), ponieważ przez większość czasu pandy muszą utworzyć instancję nowego obiektu, aby operować na nim w pełnej skali zbioru danych(tzn. nie można dodać dwóch kolumn razem, jeśli jedna jeszcze nie istnieje).

Mam nadzieję, że to pomoże:)

 8
Author: seeiespi,
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-08-19 00:08:08

Czy to działa?

capWeighting = lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum()

bdata["MarketReturn"] = bdata.groupby("yearmonth").transform(capWeighting)

Używam reindex_like do tego:

summedbdata = bdata.groupby("yearmonth").apply(lambda x: (x["PriceReturn"]*x["MarketCap"]/x["MarketCap"].sum()).sum())
summedbdata.set_index('yearmonth').reindex_like(bdata.set_index('yearmonth').sort_index(), method='ffill')
 0
Author: Def_Os,
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
2012-08-31 15:04:46