

python初心者です。  
いろいろ調べながら進めているのですが、どうしてもすっきりしないので、どなたか教えていただけませんでしょうか？

◆Pandasのデータフレームで、縦持ちのデータを横持ちに変換する方法です。

以下のような縦持ちのデータがあるとします。（csvから読み込み）

[code]

    　　ID　number　item
    0　　A　　111　　nn
    1　　A　　123　　mm
    2　　A　　211　　op
    3　　B　　121　　nm
    4　　B　　333　　nn
    
[/code]

これを、次のような横持ちデータにしたいです。  
これもデータフレームで出力したいのです。

[code]

    ID　　number　　item　　number　　item　　number　　item
    A　　　111　　　　nn　　　123　　　  mm　　　211　　　  op
    B　　　211　　　　nm　　　333　　　  nn
    
[/code]

For文などで地道に実行することはできたのですが、どうも処理速度を考えるとどうかなという気がします。  
また、拙いので、ここへの掲載を自粛いたします・・・。

よろしくお願いいたします。


CC BY-SA 3.0
2016-05-14T04:18:37.060
24845
2016-05-16T00:40:48.087
2016-05-14T07:21:13.103
15491
15491
question
python pandas
pythonのPandasで、縦持ちのデータを横持ちにするよい方法を教えてください。


> 出力はDataframeが理想です。

シンプルと言うには程遠いですが、以下の様になります。

[code]

    >>> import pandas as pd
    >>> from itertools import chain
    >>> df = pd.DataFrame([
          ['A', 111, 'nn'], ['A', 123, 'mm'], ['A', 211, 'op'],
          ['B', 121, 'nm'], ['B', 333, 'nn'],
        ], columns=['ID','number','item'])
    
    >>> df
      ID  number item
    0  A     111   nn
    1  A     123   mm
    2  A     211   op
    3  B     121   nm
    4  B     333   nn
    
    >>> df2 = df.groupby('ID')\
          .apply(
            lambda x:
              pd.DataFrame([[list(x.ID)[0]] +
                           list(chain.from_iterable(zip(x.number, x.item)))]))\
          .reset_index(drop=True)
    
    >>> df2.columns = ['ID'] + list(chain.from_iterable(
                        [['number'+str(i+1), 'item'+str(i+1)]
                           for i in range((len(df2.columns)-1)/2)]))
    
    >>> df2
      ID  number1 item1  number2 item2  number3 item3
    0  A      111    nn      123    mm      211    op
    1  B      121    nm      333    nn      NaN   NaN
    
[/code]

カラムが足りない部分は `NaN` になります。何か特定の値を入れたい場合は `fillna()` を使うと良いかと思います。


CC BY-SA 3.0
2016-05-14T06:01:00.637
24849
2016-05-14T13:04:27.560
2016-05-14T13:04:27.560
24845
answer


`groupby`でグループ化し， `apply`でグループ内の要素を結合する関数`f`を適用します．
`DataFrame`のまま結合するので同じcolumn名は使えないので，ここでは`number`,`number1`,`number2`,...のように末尾に数字を加えるた新たなcolumnにしています(`rsuffix='{0}'.format(i)`のところ).
これで以下のようにDataFrameを作れます．

[code]

    def f(a):    
        a.index = [0 for i in range(len(a))]
        del a['ID']
        out = a[0:1]
        for i in range(1, len(a)):
            out = out.join(a[i:i+1], rsuffix='{0}'.format(i))
        return out
    
    df = pd.DataFrame([['A', 111, 'nn'], ['A', 123, 'mm'], ['A', 211, 'op'],
      ['B', 121, 'nm'], ['B', 333, 'nn']], columns=['ID','number','item'])
    
    df2 = df.groupby(df['ID']).apply(f)
    
[/code]

出力してみると以下のようになっています．

[code]

    >>>> print(df2)
         item item1 item2  number  number1  number2
    ID                                             
    A  0   nn    mm    op     111      123    211.0
    B  0   nm    nn   NaN     121      333      Nan  
    
[/code]

ただこのままではindexがよけいな0がはいってMultiindexになっているのがうっとおしいので以下のようにすれば，0を消してSingleindexにできます．

[code]

    >>>> df3 = pandas.DataFrame(df2.to_records())
    >>>> del df3['level_0']
    >>>> print(df3)
     ID item item1 item2  number  number1  number2
    0  A   nn    mm    op     111      123    211.0
    1  B   nm    nn   NaN     121      333      NaN    
    
[/code]


CC BY-SA 3.0
2016-05-14T07:56:08.550
24852
2016-05-14T08:10:41.103
2016-05-14T08:10:41.103
14418
14418
24845
answer


こんなのでもできます。  
ID毎にindexを定義できるので、ある程度応用可能です。

[code]

    df["tmpindex"]=df.index
    df.tmpindex=df.groupby('ID').tmpindex.rank(ascending=True)
    
[/code]

成型はこんな感じですかね。

[code]

    df["numberindex"]=df.tmpindex.apply(lambda x: "number{}".format(int(x)))
    df["itemindex"]=df.tmpindex.apply(lambda x: "item{}".format(int(x)))
    df_pv=pd.concat(
                    [
                     df.pivot(index="ID", columns="numberindex",values="number"),
                     df.pivot(index="ID", columns="itemindex",values="item")
                     ],
                    axis=1)
    df_pv["ID"]=df_pv.index
    df_pv.reset_index(drop=True).ix[:,[-1]+list(range(df_pv.shape[1]-1))]
    
[/code]


CC BY-SA 3.0
2016-05-16T00:40:48.087
24888
2016-05-16T00:40:48.087
15508
24845
answer