2016-11-09 11 views
2

私は簡単に入力としてmutliple列からのデータを使用する関数を作成することができるように、マルチインデックスを持つデータフレームがあります。パンダはGROUPBY:複数の列を構築し、効率的

df = pd.DataFrame({('ALSN','VA.M'):range(5), ('ALSN','VB.M'):np.arange(5)+2, 
        ('ALVY','VA.M'):range(5), ('ALVY','VB.M'):np.arange(5)+20, 
        ('ALSN', 'VP.M'):np.arange(5)-10, ('ALVY','VP.M'):np.arange(5)-30, 
        ('ALGG', 'VP.M'):np.arange(5)/5.}) 

     ALGG ALSN     ALVY 
     VP.M VA.M VB.M VP.M VA.M VB.M VP.M 
0  0.0  0  2  -10  0  20  -30 
1  0.2  1  3  -9  1  21  -29 
2  0.4  2  4  -8  2  22  -28 
3  0.6  3  5  -7  3  23  -27 
4  0.8  4  6  -6  4  24  -26 

私は、これをフィルタリングしたいとし

df2 = g.filter(lambda z: z.name[-1] != 'G') 
df2.groupby(level=0, axis=1).apply(lambda g: pd.Series(g[g.name]['VB.M']+g[g.name]['VA.M'])) 

    ALSN ALVY 
0 2  20 
1 4  22 
2 6  24 
3 8  26 
4 10  28 

だから、ここまでとても良い:その後、VA.MとVB.M列、たとえば、に関数を適用します。 しかし、私が実際にやりたいことは、上記のように複数の列を入力として受け取り、複数の列を出力する関数を構築することです。たとえば、VA.MとVB.Mの列を合計し、平方根と立方根を新しい列として返します。

私は明らかに2つの異なる適用関数(合計を計算し、正方形または立方根のいずれか)でこれを行うことができましたが、中間ステップ(合計を計算)を1回だけ実行したいと思います。それは可能ですか?

さらに、出力をデータフレームに、たとえば列( 'ALSN'、 'V2')、( 'ALSN'、 'V3')で保存することができます。適用と同じくらい?または、V2列またはV3列を計算し、結果のデータフレームを取り込んで元の列にマージする必要がありますか?

答えて

1

これは簡単ではありません。

まずboolean indexingget_level_valuesによってMultiIndexの最初のレベルの値を検索し、slicersによって選択:

lvl = df.columns.get_level_values(0).unique() 
lvl = lvl[~lvl.str.contains('G')] 
print (lvl) 
Index(['ALSN', 'ALVY'], dtype='object') 

idx = pd.IndexSlice 
df2 = df.loc[:, idx[lvl, ['VA.M','VB.M']]] 
print (df2) 
    ALSN  ALVY  
    VA.M VB.M VA.M VB.M 
0 0 2 0 20 
1 1 3 1 21 
2 2 4 2 22 
3 3 5 3 23 
4 4 6 4 24 

使用groupby機能毎に、次いでunstackconcat

print (pd.concat([df2.groupby(level=0, axis=1).apply(lambda x: (x**2).sum(axis=1)), 
        df2.groupby(level=0, axis=1).apply(lambda x: (x**3).sum(axis=1))], 
        keys=('x^2','x^3')).unstack(0)) 

    ALSN  ALVY  
    x^2 x^3 x^2 x^3 
0 4 8 400 8000 
1 10 28 442 9262 
2 20 72 488 10656 
3 34 152 538 12194 
4 52 280 592 13888 

別の非常に類似し溶液:

print (df2.groupby(level=0, axis=1).apply(lambda x: (x**2))) 
    ALSN  ALVY  
    VA.M VB.M VA.M VB.M 
0 0 4 0 400 
1 1 9 1 441 
2 4 16 4 484 
3 9 25 9 529 
4 16 36 16 576 

print (df2.groupby(level=0, axis=1).apply(lambda x: (x**3))) 
    ALSN  ALVY  
    VA.M VB.M VA.M VB.M 
0 0 8 0 8000 
1 1 27 1 9261 
2 8 64 8 10648 
3 27 125 27 12167 
4 64 216 64 13824 
コメントによる
df21 = df2.groupby(level=0, axis=1).apply(lambda x: (x**2).sum(axis=1)) 
df22 = df2.groupby(level=0, axis=1).apply(lambda x: (x**3).sum(axis=1)) 
print (df21) 
    ALSN ALVY 
0  4 400 
1 10 442 
2 20 488 
3 34 538 
4 52 592 

print (df22) 
    ALSN ALVY 
0  8 8000 
1 28 9262 
2 72 10656 
3 152 12194 
4 280 13888 

print (pd.concat([df21,df22], keys=('x^2','x^3')).unstack(0)) 
    ALSN  ALVY  
    x^2 x^3 x^2 x^3 
0 4 8 400 8000 
1 10 28 442 9262 
2 20 72 488 10656 
3 34 152 538 12194 
4 52 280 592 13888 

EDIT:

df = df2.groupby(level=0, axis=1).sum() 
print (df) 
    ALSN ALVY 
0  2 20 
1  4 22 
2  6 24 
3  8 26 
4 10 28 

df3 = pd.concat([df ** 2, df ** 3], keys=('x^2','x^3'), axis=1) 
df3.columns = df3.columns.swaplevel(0,1) 
df3 = df3.sort_index(axis=1) 
print (df3) 
    ALSN  ALVY  
    x^2 x^3 x^2 x^3 
0 4  8 400 8000 
1 16 64 484 10648 
2 36 216 576 13824 
3 64 512 676 17576 
4 100 1000 784 21952 
+0

が適用され、本当に複雑で使用しないようにしてください。ユーザーはこれが正しいことだと思うでしょう。あなたのソル2の方がはるかに優れています。グループに直接適用して.sum()を使用する方が良いかもしれません。(ちょっとトリッキーですが) – Jeff

+0

@Jeff - ありがとう、試してみます。私は最初の解決策を削除します。 – jezrael

+0

@jezrael - 提案に感謝します! 2つの質問:(1)どのバージョンのパンダ/ numpyを使用していますか?鉱山はあなたの行#2の.strに満足していません。〜lvl.str.contains( 'G'); 2)どちらのソリューションでも、合計の前に 'x ** 2'または' x ** 3'を適用しています。私がしたいのは、合計を最初に実行してから次の操作を実行することです。理想的には、合計を一度計算することは望ましくありません。(x ** 2の前に1回ではなく、x.x 3 ') – SWallace

関連する問題