web-dev-qa-db-ja.com

データフレームをピボットする方法

  • ピボットとは何ですか?
  • どうやってピボットできますか?
  • これは要点ですか?
  • ワイドフォーマットからワイドフォーマットへ

私はピボットテーブルについて尋ねる質問をたくさん見ました。ピボットテーブルについて質問していることを知らなくても、通常はそうです。ピボットのあらゆる側面を網羅した標準的な質問と回答を書くことは事実上不可能です。

...しかし、私はそれを試してみるつもりです。


既存の質問と回答の問題点は、多くの場合既存の良い回答を使用するためにOPが一般化に問題を抱えているというニュアンスに問題が集中していることです。しかし、答えのどれも包括的な説明をするように試みません(それが困難な仕事であるので)

私の グーグル検索 からいくつかの例を見てください

  1. Pandasでデータフレームをピボットするにはどうすればいいですか?
    • 良い質問と答えです。しかし、答えは特定の質問にほとんど説明のない答えを出すだけです。
  2. パンダテーブルとデータフレーム
    • この質問では、OPはピボットの出力に関係しています。つまり、列はどのように見えますか。 OPはそれがRのように見えることを望んでいました。これはパンダユーザーにとってはあまり役に立ちません。
  3. パンダはデータフレームをピボットし、行を複製します
    • もう1つの適切な質問ですが、その答えは1つの方法、つまりpd.DataFrame.pivotに焦点を当てています。

そのため、誰かがpivotを検索すると、散発的な結果になり、特定の質問に答えられない可能性があります。


セットアップ

あなたが私が私のコラムと関連コラム値を私が以下の答えでどのようにピボットするつもりであるかと対応するために顕著に命名したのに気付くかもしれません。探している結果を得るために、どの列名がどこに行くのかをよく理解できるように注意してください。

import numpy as np
import pandas as pd
from numpy.core.defchararray import add

np.random.seed([3,1415])
n = 20

cols = np.array(['key', 'row', 'item', 'col'])
arr1 = (np.random.randint(5, size=(n, 4)) // [2, 1, 2, 1]).astype(str)

df = pd.DataFrame(
    add(cols, arr1), columns=cols
).join(
    pd.DataFrame(np.random.Rand(n, 2).round(2)).add_prefix('val')
)
print(df)

     key   row   item   col  val0  val1
0   key0  row3  item1  col3  0.81  0.04
1   key1  row2  item1  col2  0.44  0.07
2   key1  row0  item1  col0  0.77  0.01
3   key0  row4  item0  col2  0.15  0.59
4   key1  row0  item2  col1  0.81  0.64
5   key1  row2  item2  col4  0.13  0.88
6   key2  row4  item1  col3  0.88  0.39
7   key1  row4  item1  col1  0.10  0.07
8   key1  row0  item2  col4  0.65  0.02
9   key1  row2  item0  col2  0.35  0.61
10  key2  row0  item2  col1  0.40  0.85
11  key2  row4  item1  col2  0.64  0.25
12  key0  row2  item2  col3  0.50  0.44
13  key0  row4  item1  col4  0.24  0.46
14  key1  row3  item2  col3  0.28  0.11
15  key0  row3  item1  col1  0.31  0.23
16  key0  row0  item2  col3  0.86  0.01
17  key0  row4  item0  col3  0.64  0.21
18  key2  row2  item2  col0  0.13  0.45
19  key0  row2  item0  col4  0.37  0.70

質問

  1. なぜValueError: Index contains duplicate entries, cannot reshapeが出るのですか

  2. df値が列、col値がインデックス、そしてval0の平均が値になるようにrowをピボットするにはどうすればよいですか?

    col   col0   col1   col2   col3  col4
    row                                  
    row0  0.77  0.605    NaN  0.860  0.65
    row2  0.13    NaN  0.395  0.500  0.25
    row3   NaN  0.310    NaN  0.545   NaN
    row4   NaN  0.100  0.395  0.760  0.24
    
  3. df値が列、col値がインデックス、val0の平均が値、欠損値が0となるようにrowをピボットする方法を教えてください。

    col   col0   col1   col2   col3  col4
    row                                  
    row0  0.77  0.605  0.000  0.860  0.65
    row2  0.13  0.000  0.395  0.500  0.25
    row3  0.00  0.310  0.000  0.545  0.00
    row4  0.00  0.100  0.395  0.760  0.24
    
  4. おそらくmeanのように、sum以外のものを手に入れることができますか?

    col   col0  col1  col2  col3  col4
    row                               
    row0  0.77  1.21  0.00  0.86  0.65
    row2  0.13  0.00  0.79  0.50  0.50
    row3  0.00  0.31  0.00  1.09  0.00
    row4  0.00  0.10  0.79  1.52  0.24
    
  5. 一度に複数の集計を実行できますか?

           sum                          mean                           
    col   col0  col1  col2  col3  col4  col0   col1   col2   col3  col4
    row                                                                
    row0  0.77  1.21  0.00  0.86  0.65  0.77  0.605  0.000  0.860  0.65
    row2  0.13  0.00  0.79  0.50  0.50  0.13  0.000  0.395  0.500  0.25
    row3  0.00  0.31  0.00  1.09  0.00  0.00  0.310  0.000  0.545  0.00
    row4  0.00  0.10  0.79  1.52  0.24  0.00  0.100  0.395  0.760  0.24
    
  6. 複数の値列にわたって集計できますか?

          val0                             val1                          
    col   col0   col1   col2   col3  col4  col0   col1  col2   col3  col4
    row                                                                  
    row0  0.77  0.605  0.000  0.860  0.65  0.01  0.745  0.00  0.010  0.02
    row2  0.13  0.000  0.395  0.500  0.25  0.45  0.000  0.34  0.440  0.79
    row3  0.00  0.310  0.000  0.545  0.00  0.00  0.230  0.00  0.075  0.00
    row4  0.00  0.100  0.395  0.760  0.24  0.00  0.070  0.42  0.300  0.46
    
  7. 複数の列で分割できますか?

    item item0             item1                         item2                   
    col   col2  col3  col4  col0  col1  col2  col3  col4  col0   col1  col3  col4
    row                                                                          
    row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.605  0.86  0.65
    row2  0.35  0.00  0.37  0.00  0.00  0.44  0.00  0.00  0.13  0.000  0.50  0.13
    row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.000  0.28  0.00
    row4  0.15  0.64  0.00  0.00  0.10  0.64  0.88  0.24  0.00  0.000  0.00  0.00
    
  8. または

    item      item0             item1                         item2                  
    col        col2  col3  col4  col0  col1  col2  col3  col4  col0  col1  col3  col4
    key  row                                                                         
    key0 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.86  0.00
         row2  0.00  0.00  0.37  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.50  0.00
         row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.00  0.00  0.00
         row4  0.15  0.64  0.00  0.00  0.00  0.00  0.00  0.24  0.00  0.00  0.00  0.00
    key1 row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.81  0.00  0.65
         row2  0.35  0.00  0.00  0.00  0.00  0.44  0.00  0.00  0.00  0.00  0.00  0.13
         row3  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.28  0.00
         row4  0.00  0.00  0.00  0.00  0.10  0.00  0.00  0.00  0.00  0.00  0.00  0.00
    key2 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.40  0.00  0.00
         row2  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.13  0.00  0.00  0.00
         row4  0.00  0.00  0.00  0.00  0.00  0.64  0.88  0.00  0.00  0.00  0.00  0.00
    
  9. 列と行が一緒に発生する頻度、いわゆる「クロス集計」を集計できますか?

    col   col0  col1  col2  col3  col4
    row                               
    row0     1     2     0     1     1
    row2     1     0     2     1     2
    row3     0     1     0     2     0
    row4     0     1     2     2     1
    
  10. 2列のみを中心にしてDataFrameをlongからwideに変換する方法を教えてください。与えられた、

    np.random.seed([3, 1415])
    df2 = pd.DataFrame({'A': list('aaaabbbc'), 'B': np.random.choice(15, 8)})        
    df2        
       A   B
    0  a   0
    1  a  11
    2  a   2
    3  a  11
    4  b  10
    5  b  10
    6  b  14
    7  c   7
    

    予想されるものは次のようになります。

          a     b    c
    0   0.0  10.0  7.0
    1  11.0  10.0  NaN
    2   2.0  14.0  NaN
    3  11.0   NaN  NaN
    
250
piRSquared

最初の質問に答えることから始めます。

質問1

なぜValueError: Index contains duplicate entries, cannot reshapeが出るのですか

これは、パンダがcolumnsまたはindexのいずれかのオブジェクトに重複するエントリを付けてインデックスを再作成しようとしているために発生します。ピボットを実行できるさまざまな使用方法があります。それらのうちのいくつかはそれがピボットするように求められているキーの重複があるときにはあまり適していません。例えば。 pd.DataFrame.pivotを考えます。 rowcolの値を共有する重複エントリがあることを私は知っています。

df.duplicated(['row', 'col']).any()

True

だから私はpivotを使って

df.pivot(index='row', columns='col', values='val0')

上記のエラーが出ます。実際、同じタスクを実行しようとすると同じエラーが発生します。

df.set_index(['row', 'col'])['val0'].unstack()

これは私たちがピボットするために使用できる慣用句のリストです。

  1. pd.DataFrame.groupby + pd.DataFrame.unstack
    • あらゆる種類のピボットについて行うための優れた一般的なアプローチ
    • 1つのグループ内でピボット行レベルと列レベルを構成するすべての列をで指定します。それには、集計したい残りの列と集計を実行したい機能を選択します。最後に、列インデックスに含めるレベルをunstackにします。
  2. pd.DataFrame.pivot_table
    • より直感的なAPIを持つgroupbyの賛美バージョン。多くの人にとって、これは好ましいアプローチです。そしてそれは開発者による意図的なアプローチです。
    • 行レベル、列レベル、集約する値、および集約を実行するための関数を指定します。
  3. pd.DataFrame.set_index + pd.DataFrame.unstack
    • ある人にとっては便利で直感的です(私自身も含みます)。重複してグループ化されたキーを処理することはできません。
    • groupbyパラダイムと同様に、最終的に行または列レベルになるすべての列を指定し、それらをインデックスに設定します。次に、列に必要なレベルをunstackにします。残りのインデックスレベルまたは列レベルが一意でない場合、このメソッドは失敗します。
  4. pd.DataFrame.pivot
    • 重複キーの制限を共有するという点でset_indexと非常によく似ています。 APIも非常に限られています。 indexcolumnsvaluesのスカラー値のみを取ります。
    • 行、列、およびピボットする値を選択するという点でpivot_tableメソッドと似ています。ただし、集計することはできず、行または列のいずれかが一意でないと、このメソッドは失敗します。
  5. pd.crosstab
    • これはpivot_tableの特別バージョンであり、最も純粋な形式はいくつかのタスクを実行するための最も直感的な方法です。
  6. pd.factorize + np.bincount
    • これは非常にあいまいですが非常に高速な非常に高度な手法です。それはすべての状況で使用することはできませんが、それが使用することができ、あなたがそれを使用して快適であれば、あなたはパフォーマンスの報酬を享受します。
  7. pd.get_dummies + pd.DataFrame.dot
    • 私はこれを巧みにクロス集計を行うために使用します。

それぞれの答えと質問に対して私がやることはpd.DataFrame.pivot_tableを使ってそれに答えることです。それから私は同じタスクを実行するための代替手段を提供します。

質問3

dfの値が列、colの値がインデックス、val0の平均が値、欠損値が0となるようにrowをピボットする方法を教えてください。

  • pd.DataFrame.pivot_table

    • fill_valueはデフォルトでは設定されていません。適切に設定する傾向があります。この場合は0に設定しました。question 2はスキップしたことに注意してください。これはfill_valueを除いてこの回答と同じです。
    • aggfunc='mean'がデフォルトであり、設定する必要はありませんでした。私はそれを明確にするために含めました。

      df.pivot_table(
          values='val0', index='row', columns='col',
          fill_value=0, aggfunc='mean')
      
      col   col0   col1   col2   col3  col4
      row                                  
      row0  0.77  0.605  0.000  0.860  0.65
      row2  0.13  0.000  0.395  0.500  0.25
      row3  0.00  0.310  0.000  0.545  0.00
      row4  0.00  0.100  0.395  0.760  0.24
      
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].mean().unstack(fill_value=0)
    
  • pd.crosstab

    pd.crosstab(
        index=df['row'], columns=df['col'],
        values=df['val0'], aggfunc='mean').fillna(0)
    

質問4

おそらくmeanのように、sum以外のものを入手できますか?

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index='row', columns='col',
        fill_value=0, aggfunc='sum')
    
    col   col0  col1  col2  col3  col4
    row                               
    row0  0.77  1.21  0.00  0.86  0.65
    row2  0.13  0.00  0.79  0.50  0.50
    row3  0.00  0.31  0.00  1.09  0.00
    row4  0.00  0.10  0.79  1.52  0.24
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].sum().unstack(fill_value=0)
    
  • pd.crosstab

    pd.crosstab(
        index=df['row'], columns=df['col'],
        values=df['val0'], aggfunc='sum').fillna(0)
    

質問5

一度に複数の集計を実行できますか?

pivot_tablecross_tabでは、呼び出し可能オブジェクトのリストを渡す必要があることに注意してください。一方、groupby.aggは限られた数の特殊関数に対して文字列を取ることができます。 groupby.aggも他の人に渡したのと同じ呼び出し可能オブジェクトを使用していたでしょうが、効率を上げるために文字列関数名を利用するほうがより効率的です。

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index='row', columns='col',
        fill_value=0, aggfunc=[np.size, np.mean])
    
         size                      mean                           
    col  col0 col1 col2 col3 col4  col0   col1   col2   col3  col4
    row                                                           
    row0    1    2    0    1    1  0.77  0.605  0.000  0.860  0.65
    row2    1    0    2    1    2  0.13  0.000  0.395  0.500  0.25
    row3    0    1    0    2    0  0.00  0.310  0.000  0.545  0.00
    row4    0    1    2    2    1  0.00  0.100  0.395  0.760  0.24
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].agg(['size', 'mean']).unstack(fill_value=0)
    
  • pd.crosstab

    pd.crosstab(
        index=df['row'], columns=df['col'],
        values=df['val0'], aggfunc=[np.size, np.mean]).fillna(0, downcast='infer')
    

質問6

複数の値列にわたって集計できますか?

  • pd.DataFrame.pivot_table私達はvalues=['val0', 'val1']を渡します、しかし我々はそれを完全に止めたかもしれません

    df.pivot_table(
        values=['val0', 'val1'], index='row', columns='col',
        fill_value=0, aggfunc='mean')
    
          val0                             val1                          
    col   col0   col1   col2   col3  col4  col0   col1  col2   col3  col4
    row                                                                  
    row0  0.77  0.605  0.000  0.860  0.65  0.01  0.745  0.00  0.010  0.02
    row2  0.13  0.000  0.395  0.500  0.25  0.45  0.000  0.34  0.440  0.79
    row3  0.00  0.310  0.000  0.545  0.00  0.00  0.230  0.00  0.075  0.00
    row4  0.00  0.100  0.395  0.760  0.24  0.00  0.070  0.42  0.300  0.46
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0', 'val1'].mean().unstack(fill_value=0)
    

質問7

複数の列で分割できますか?

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index='row', columns=['item', 'col'],
        fill_value=0, aggfunc='mean')
    
    item item0             item1                         item2                   
    col   col2  col3  col4  col0  col1  col2  col3  col4  col0   col1  col3  col4
    row                                                                          
    row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.605  0.86  0.65
    row2  0.35  0.00  0.37  0.00  0.00  0.44  0.00  0.00  0.13  0.000  0.50  0.13
    row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.000  0.28  0.00
    row4  0.15  0.64  0.00  0.00  0.10  0.64  0.88  0.24  0.00  0.000  0.00  0.00
    
  • pd.DataFrame.groupby

    df.groupby(
        ['row', 'item', 'col']
    )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
    

質問8

複数の列で分割できますか?

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index=['key', 'row'], columns=['item', 'col'],
        fill_value=0, aggfunc='mean')
    
    item      item0             item1                         item2                  
    col        col2  col3  col4  col0  col1  col2  col3  col4  col0  col1  col3  col4
    key  row                                                                         
    key0 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.86  0.00
         row2  0.00  0.00  0.37  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.50  0.00
         row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.00  0.00  0.00
         row4  0.15  0.64  0.00  0.00  0.00  0.00  0.00  0.24  0.00  0.00  0.00  0.00
    key1 row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.81  0.00  0.65
         row2  0.35  0.00  0.00  0.00  0.00  0.44  0.00  0.00  0.00  0.00  0.00  0.13
         row3  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.28  0.00
         row4  0.00  0.00  0.00  0.00  0.10  0.00  0.00  0.00  0.00  0.00  0.00  0.00
    key2 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.40  0.00  0.00
         row2  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.13  0.00  0.00  0.00
         row4  0.00  0.00  0.00  0.00  0.00  0.64  0.88  0.00  0.00  0.00  0.00  0.00
    
  • pd.DataFrame.groupby

    df.groupby(
        ['key', 'row', 'item', 'col']
    )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
    
  • キーのセットは行と列の両方に対して一意であるため、pd.DataFrame.set_index

    df.set_index(
        ['key', 'row', 'item', 'col']
    )['val0'].unstack(['item', 'col']).fillna(0).sort_index(1)
    

質問9

列と行が一緒に発生する頻度、いわゆる「クロス集計」を集計できますか?

  • pd.DataFrame.pivot_table

    df.pivot_table(index='row', columns='col', fill_value=0, aggfunc='size')
    
        col   col0  col1  col2  col3  col4
    row                               
    row0     1     2     0     1     1
    row2     1     0     2     1     2
    row3     0     1     0     2     0
    row4     0     1     2     2     1
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].size().unstack(fill_value=0)
    
  • pd.cross_tab

    pd.crosstab(df['row'], df['col'])
    
  • pd.factorize + np.bincount

    # get integer factorization `i` and unique values `r`
    # for column `'row'`
    i, r = pd.factorize(df['row'].values)
    # get integer factorization `j` and unique values `c`
    # for column `'col'`
    j, c = pd.factorize(df['col'].values)
    # `n` will be the number of rows
    # `m` will be the number of columns
    n, m = r.size, c.size
    # `i * m + j` is a clever way of counting the 
    # factorization bins assuming a flat array of length
    # `n * m`.  Which is why we subsequently reshape as `(n, m)`
    b = np.bincount(i * m + j, minlength=n * m).reshape(n, m)
    # BTW, whenever I read this, I think 'Bean, Rice, and Cheese'
    pd.DataFrame(b, r, c)
    
          col3  col2  col0  col1  col4
    row3     2     0     0     1     0
    row2     1     2     1     0     2
    row0     1     0     1     2     1
    row4     2     2     0     1     1
    
  • pd.get_dummies

    pd.get_dummies(df['row']).T.dot(pd.get_dummies(df['col']))
    
          col0  col1  col2  col3  col4
    row0     1     2     0     1     1
    row2     1     0     2     1     2
    row3     0     1     0     2     0
    row4     0     1     2     2     1
    

質問10

2列のみを中心にしてDataFrameをlongからwideに変換する方法を教えてください。

最初のステップは各行に番号を割り当てることです - この番号はピボットされた結果のその値の行インデックスになります。これは GroupBy.cumcount を使って行われます。

df2.insert(0, 'count', df.groupby('A').cumcount())
df2

   count  A   B
0      0  a   0
1      1  a  11
2      2  a   2
3      3  a  11
4      0  b  10
5      1  b  10
6      2  b  14
7      0  c   7

2番目のステップは、 DataFrame.pivot を呼び出すためのインデックスとして、新しく作成した列を使用することです。

df2.pivot(*df)
# df.pivot(index='count', columns='A', values='B')

A         a     b    c
count                 
0       0.0  10.0  7.0
1      11.0  10.0  NaN
2       2.0  14.0  NaN
3      11.0   NaN  NaN

これを一行で行うには、assignを使用します。

df2.assign(count=df.groupby('A').cumcount()).pivot('count', 'A', 'B')

A         a     b    c
count                 
0      12.0  11.0  9.0
1       5.0   3.0  NaN
2       0.0   7.0  NaN
3       3.0   NaN  NaN
214
piRSquared