s = pd.Series(np.random.randn(6))
s[::2] = np.nan
s
0 NaN
1 0.050009
2 NaN
3 0.852965
4 NaN
5 -0.023493
dtype: float64
랜덤으로 6개의 숫자를 생성한 후, s에 Series로 할당했고,
2배 간격으로 0, 2, 4의 row에 NaN을 넣었습니다.
s.fillna(s.mean())
0 0.293160
1 0.050009
2 0.293160
3 0.852965
4 0.293160
5 -0.023493
dtype: float64
s의 평균을 fillna를 통해 넣었습니다.
또 다른 예시를 봅시다.
states = ['Ohio', 'New York', 'Vermont', 'Florida',
'Oregon', 'Nevada', 'California', 'Idaho']
group_key = ['East'] * 4 + ['West'] * 4
data = pd.Series(np.random.randn(8), index=states)
data
Ohio -2.304234
New York -0.652469
Vermont -1.218302
Florida -1.332610
Oregon 1.074623
Nevada 0.723642
California 0.690002
Idaho 1.001543
dtype: float64
index에 states를 넣은 모습입니다.
data[['Vermont', 'Nevada', 'Idaho']] = np.nan
data
Ohio -2.304234
New York -0.652469
Vermont NaN
Florida -1.332610
Oregon 1.074623
Nevada NaN
California 0.690002
Idaho NaN
dtype: float64
이후 Vermont, ~~~ 에 NaN 데이터를 넣었습니다.
data.groupby(group_key).mean()
East -1.429771
West 0.882312
dtype: float64
그 다음 group_key를 통해 각각 east, west를 4개씩 할당했고,
할당을 기준으로 그룹화 했습니다.
이후 그룹화된 그룹으로 평균을 출력하는 모습입니다.
fill_mean = lambda g: g.fillna(g.mean())
data.groupby(group_key).apply(fill_mean)
Ohio -2.304234
New York -0.652469
Vermont -1.429771
Florida -1.332610
Oregon 1.074623
Nevada 0.882312
California 0.690002
Idaho 0.882312
dtype: float64
아예 람다 함수를 만들어서 적용하는 모습입니다.
이때는 group_key를 기준으로 그룹화 했으므로, NaN 값에는 각 east, west의 인덱스에 해당하는 평균 값이 들어갑니다.
fill_values = {'East': 0.5, 'West': -1}
fill_func = lambda g: g.fillna(fill_values[g.name])
data.groupby(group_key).apply(fill_func)
Ohio -2.304234
New York -0.652469
Vermont 0.500000
Florida -1.332610
Oregon 1.074623
Nevada -1.000000
California 0.690002
Idaho -1.000000
dtype: float64
# Hearts, Spades, Clubs, Diamonds
suits = ['H', 'S', 'C', 'D']
card_val = (list(range(1, 11)) + [10] * 3) * 4
base_names = ['A'] + list(range(2, 11)) + ['J', 'K', 'Q']
cards = []
for suit in ['H', 'S', 'C', 'D']:
cards.extend(str(num) + suit for num in base_names)
deck = pd.Series(card_val, index=cards)
deck[:13]
AH 1
2H 2
3H 3
4H 4
5H 5
6H 6
7H 7
8H 8
9H 9
10H 10
JH 10
KH 10
QH 10
dtype: int64
우선 card_val에는 [1, 2, 3, 4, ,,,, 10, 10, 10, 10]이 4개나 들어가있고,
base_names에는 A, 1, 2, 3, ,,, 10, J, Q, K가 들어가 있습니다.
이후 for loop를 통해 card에 값을 넣어줍니다.
마지막으로 생성된 card를 인덱스로 하고, card_val을 데이터로 하는 Series를 생성했습니다.
def draw(deck, n=5):
return deck.sample(n)
draw(deck)
AD 1
8C 8
5H 5
KC 10
2C 2
dtype: int64
무작위로 5개를 뽑았습니다.
get_suit = lambda card: card[-1] # last letter is suit
deck.groupby(get_suit).apply(draw, n=2)
C 2C 2
3C 3
D KD 10
8D 8
H KH 10
3H 3
S 2S 2
4S 4
dtype: int64
우선 get_suit는 마지막 문자를 대상으로 그룹화 하는 함수입니다.
이후, draw를 적용한 모습입니다.
deck.groupby(get_suit, group_keys=False).apply(draw, n=2)
KC 10
JC 10
AD 1
5D 5
5H 5
6H 6
7S 7
KS 10
dtype: int64
group_keys = False로 하니까 위의 계층적 인덱싱이 사라졌습니다.
df = pd.DataFrame({'category': ['a', 'a', 'a', 'a',
'b', 'b', 'b', 'b'],
'data': np.random.randn(8),
'weights': np.random.rand(8)})
df
category data weights
0 a 1.561587 0.957515
1 a 1.219984 0.347267
2 a -0.482239 0.581362
3 a 0.315667 0.217091
4 b -0.047852 0.894406
5 b -0.454145 0.918564
6 b -0.556774 0.277825
7 b 0.253321 0.955905
grouped = df.groupby('category')
get_wavg = lambda g: np.average(g['data'], weights=g['weights'])
grouped.apply(get_wavg)
category
a 0.811643
b -0.122262
dtype: float64
카테고리로 그룹화 했고, weight를 통해 가중치를 주고, 평균을 구한 모습입니다.
close_px = pd.read_csv('examples/stock_px_2.csv', parse_dates=True,
index_col=0)
close_px.info()
close_px[-4:]
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2214 entries, 2003-01-02 to 2011-10-14
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 AAPL 2214 non-null float64
1 MSFT 2214 non-null float64
2 XOM 2214 non-null float64
3 SPX 2214 non-null float64
dtypes: float64(4)
memory usage: 86.5 KB
AAPL MSFT XOM SPX
2011-10-11 400.29 27.00 76.27 1195.54
2011-10-12 402.19 26.96 77.16 1207.25
2011-10-13 408.43 27.18 76.37 1203.66
2011-10-14 422.00 27.27 78.11 1224.58
데이터를 불러왔습니다.
spx_corr = lambda x: x.corrwith(x['SPX'])
rets = close_px.pct_change().dropna()
get_year = lambda x: x.year
by_year = rets.groupby(get_year)
by_year.apply(spx_corr)
AAPL MSFT XOM SPX
2003 0.541124 0.745174 0.661265 1.0
2004 0.374283 0.588531 0.557742 1.0
2005 0.467540 0.562374 0.631010 1.0
2006 0.428267 0.406126 0.518514 1.0
2007 0.508118 0.658770 0.786264 1.0
2008 0.681434 0.804626 0.828303 1.0
2009 0.707103 0.654902 0.797921 1.0
2010 0.710105 0.730118 0.839057 1.0
2011 0.691931 0.800996 0.859975 1.0
자세히는 모르겠지만, rets는 close_px에 데이터를 퍼센트화 시키고, 결측치를 없앤 것 같습니다.
이후 이 데이터를 year을 기준으로 그룹화 시키고, 거기에 'SPX'와의 상관관계에 따라 정렬한 것 같습니다.
by_year.apply(lambda g: g['AAPL'].corr(g['MSFT']))
2003 0.480868
2004 0.259024
2005 0.300093
2006 0.161735
2007 0.417738
2008 0.611901
2009 0.432738
2010 0.571946
2011 0.581987
dtype: float64
여기서는 AAPL에 있어서, MSFT의 상관관계를 나타낸 것 같습니다.
import statsmodels.api as sm
def regress(data, yvar, xvars):
Y = data[yvar]
X = data[xvars]
X['intercept'] = 1.
result = sm.OLS(Y, X).fit()
return result.params
by_year.apply(regress, 'AAPL', ['SPX'])
SPX intercept
2003 1.195406 0.000710
2004 1.363463 0.004201
2005 1.766415 0.003246
2006 1.645496 0.000080
2007 1.198761 0.003438
2008 0.968016 -0.001110
2009 0.879103 0.002954
2010 1.052608 0.001261
2011 0.806605 0.001514
음...