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. 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
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
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).
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:)
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')
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