1. ホーム
  2. python

[解決済み] データフレームをピボットするにはどうしたらいいですか?

2022-03-19 14:57:21

質問

  • ピボットとは何ですか?
  • ピボットの方法を教えてください。
  • これはピボットですか?
  • 長尺物から幅広物へ?

ピボットテーブルについて尋ねる質問をよく見かけるようになりました。 ピボットテーブルについて尋ねていることに気づいていない場合でも、たいていはそうです。 ピボットテーブルのすべての側面を網羅する標準的な質問と回答を書くことは、事実上不可能です...

... でも、やってみようと思うんです。


既存の質問と回答の問題は、しばしば質問がニュアンスに集中しており、OPが既存の良い回答のいくつかを使用するために一般化するのに苦労していることである。 しかし、どの回答も包括的な説明をしようとはしていません(それは困難な作業であるため)。

私の Google検索

  1. Pandasでデータフレームをピボット化する方法は?
  • 良い質問と回答です。 しかし、回答は具体的な質問に答えるだけで、ほとんど説明がない。
  1. パンダのピボット・テーブルからデータ・フレーム
  • この質問では、OP はピボットの出力に関心を寄せています。 つまり、列がどのように見えるかです。 OPは、Rのように見えることを望んでいました。これは、pandasユーザーにとってあまり有益ではありません。
  1. pandas pivoting a dataframe, duplicate rows.
  • もう一つのまともな質問ですが、答えは一つの方法、すなわち 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 の値は列である。 row のインデックス、および平均値を表します。 val0 は値ですか?

     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 の値は列である。 row の平均値はインデックスです。 val0 は値、欠損値は 0 ?

     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列のみでピボットしてロングからワイドに変換するにはどうすればよいですか?与えられた。

    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
    
    
  11. の後、複数のインデックスを単一インデックスにフラット化するにはどうすればよいですか? pivot ?

    から

       1  2
       1  1  2
    a  2  1  1
    b  2  1  0
    c  1  0  0
    
    

    への

       1|1  2|1  2|2
    a    2    1    1
    b    2    1    0
    c    1    0    0
    
    

解決方法は?

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

質問1

なぜ ValueError: Index contains duplicate entries, cannot reshape

この現象は、pandasが以下のいずれかのインデックスを再作成しようとしたために発生します。 columns または index オブジェクトのエントリが重複している場合。 ピボットを実行するために使用するメソッドはさまざまです。 中には、ピボットを要求されるキーに重複がある場合に適さないものもあります。 たとえば、以下のような場合です。 を考えてみましょう。 pd.DataFrame.pivot . を共有する重複したエントリーがあることは知っています。 rowcol の値を指定します。

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

True

そこで、私が pivot を使って

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

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

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

以下は、ピボットに使えるイディオムのリストです。

  1. pd.DataFrame.groupby + pd.DataFrame.unstack

    • あらゆるタイプのピボットを行うための一般的な方法です。
    • ピボットされた行レベルおよび列レベルを構成するすべての列を、1つのグループごとに指定します。 続いて、集約する残りの列と、集約を実行する関数を選択します。 最後に unstack は、列インデックスにしたいレベルを指定します。
  2. pd.DataFrame.pivot_table

    • の美化版。 groupby より直感的なAPIで 多くの人にとって、これは好ましいアプローチです。 そして、開発者が意図したアプローチでもあります。
    • 行レベル、列レベル、集約する値、および集約を実行する関数を指定します。
  3. pd.DataFrame.set_index + pd.DataFrame.unstack

    • 便利で直感的な人もいる(私も含めて)。 グループ化されたキーの重複を扱えない。
    • と似ています。 groupby パラダイムで、最終的に行レベルまたは列レベルになるすべての列を指定し、それらをインデックスに設定します。 次に unstack には、列の中に必要なレベルを指定します。 残りのインデックスレベルまたはカラムレベルのいずれかが一意でない場合、このメソッドは失敗します。
  4. pd.DataFrame.pivot

    • と非常によく似ています。 set_index 重複キーの制限を共有している点です。 APIも非常に限定的である。 にはスカラー値しか使えない。 index , columns , values .
    • と同様に 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 の値は列である。 row の平均値はインデックスです。 val0 は値で、欠損値は 0 ?

  • pd.DataFrame.pivot_table

    • fill_value はデフォルトでは設定されていません。 適当に設定することが多いです。 この場合、私はこれを 0 . をスキップしていることに注意してください。 質問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_tablecrosstab callableのリストを渡す必要がありました。 一方 groupby.agg は、限られた特殊な関数のために文字列を取ることができます。 groupby.agg も他の人に渡したのと同じcallableを取ることができましたが、文字列関数名を活用した方が効率的なことが多いので、文字列関数名を活用することにしました。

  • 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.crosstab

      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.pivot

    まず、各行に番号を割り当てます。この番号は、回転させた結果におけるその値の行インデックスとなります。これは GroupBy.cumcount :

      df2.insert(0, 'count', df2.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(*df2)
      # df2.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
    
    
  • DataFrame.pivot_table

    一方 DataFrame.pivot はカラムのみ受け付けます。 DataFrame.pivot_table は配列も受け付けるので GroupBy.cumcount として直接渡すことができます。 index は、明示的にカラムを作成することなく

      df2.pivot_table(index=df2.groupby('A').cumcount(), columns='A', values='B')
    
      A         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
    
    

質問11

の後、マルチインデックスをシングルインデックスにフラット化するにはどうすればよいですか? pivot

もし columns タイプ object 文字列付き join

df.columns = df.columns.map('|'.join)

さもなくば format

df.columns = df.columns.map('{0[0]}|{0[1]}'.format)