如何对数据框进行透视操作?

617
什么是透视表? 如何进行透视? 如何将长格式转换为宽格式?
我看到很多关于透视表的问题,即使提问者自己并不知道。要写一个涵盖透视的所有方面的标准问题和答案几乎是不可能的...但是我还是要试一试。
现有问题和答案的问题在于,往往问题的焦点是一个细微之处,而提问者很难将其概括为多个现有好答案的使用。然而,没有一个答案试图给出全面的解释(因为这是一项艰巨的任务)。看看我在Google搜索中的几个例子:
  1. 如何在Pandas中進行資料框架的樞紐轉換? - 好問題和答案。但是答案只回答了具體問題,解釋很少。
  2. pandas樞紐轉換表轉換為資料框架 - 提問者關注樞紐轉換的輸出,即列的外觀。提問者希望它的外觀像R一樣。對於Pandas用戶來說,這並不是非常有幫助。
  3. Pandas資料框架的樞紐轉換,重複的行 - 另一個不錯的問題,但答案只關注一種方法,即pd.DataFrame.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. Why do I get ValueError: Index contains duplicate entries, cannot reshape?

  2. How do I pivot df such that the col values are columns, row values are the index, and mean of val0 are the values?

    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. How do I make it so that missing values are 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. Can I get something other than mean, like maybe 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. Can I do more that one aggregation at a time?

           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. Can I aggregate over multiple value columns?

          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. Can I subdivide by multiple columns?

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

    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. Can I aggregate the frequency in which the column and rows occur together, aka "cross tabulation"?

    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. How do I convert a DataFrame from long to wide by pivoting on ONLY two columns? Given,

    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
    

    The expected should look something like

          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. How do I flatten the multiple index to single index after pivot?

    From

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

    To

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

5
非常有用的问题!小建议:把这些问题分成几个帖子不是更合适吗?我遇到了类似第8个问题的问题,但在浏览一下后没有找到它。只有在我创建了一个(现在被标记为重复)问题后,我才再次被重定向到这里,并找到了我需要的解决方案。 - Viktor
2
在我看来,这个问题太宽泛了,不适合作为一个好的规范问题,应该将其拆分。虽然我不是Pandas专家,但我的直觉是问题2-6应该保留在这里,而问题1、7-8、9、10和11都应该单独处理。但可以使用相同的示例数据并将它们链接在一起。我愿意在Meta上讨论这个问题。 - wjandrea
@wjandrea 这个问题不应该在需要这样一个规范之前先有冗长的元评论。理想情况下,所有这些都应该在 Meta 上进行,但你无法让 SMEs 聚集在那里并进行讨论;此外,格式也不适合那种类型的讨论。我们真的需要一种环境,在那里人们可以实时协作编写 Markdown 文档,并进行聊天。 - Karl Knechtel
5个回答

469
这是一个我们可以用来转折的成语列表。
  1. pd.DataFrame.pivot_table

    • 一个更直观的API,是groupby的升级版。对于很多人来说,这是首选的方法,也是开发者预期的方法。
    • 指定行级别、列级别、要聚合的值以及要执行的聚合函数。
  2. pd.DataFrame.groupby + pd.DataFrame.unstack

    • 适用于几乎任何类型的数据透视的通用方法。
    • 在一个group by中指定所有将构成透视行级别和列级别的列。然后选择要聚合的剩余列和要执行的聚合函数。最后,使用unstack将要放入列索引中的级别。
  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

    • 我用它来巧妙地执行交叉制表。
另请参阅:

问题1

为什么我会得到ValueError: Index contains duplicate entries, cannot reshape的错误?

这是因为pandas试图对具有重复条目的columnsindex对象进行重新索引。在执行数据透视时,有多种方法可供选择。其中一些方法不适用于存在重复键的情况。例如:考虑使用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()

例子

对于每个后续的问题,我将使用pd.DataFrame.pivot_table来回答。然后,我会提供执行相同任务的替代方法。

问题2和问题3

如何将df进行透视,使得col的值成为列,row的值成为索引,val0的均值成为值?

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index='row', columns='col',
        aggfunc='mean')
    
    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
    
    • aggfunc='mean' 是默认值,我不需要设置它。我包含它只是为了明确起见。
如何将缺失值设为0?
  • pd.DataFrame.pivot_table

    • fill_value默认情况下未设置。我倾向于适当设置它。在这种情况下,我将其设置为0
    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,我需要传递可调用对象的列表。另一方面,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 because the set of keys are unique for both rows and columns

    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题
如何通过仅在两列上进行透视,将DataFrame从长格式转换为宽格式?
  • 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
    

    第二步是使用新创建的列作为索引来调用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后将多级索引转换为单级索引?
如果列的类型是对象,并且需要使用字符串连接。
df.columns = df.columns.map('|'.join)

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

2
pivot_table()crosstab() 现在可以接受字符串函数名作为参数,虽然我不确定这个更改是何时发生的,因为文档并没有说明得很清楚。我正在使用 Pandas 1.4.4 版本。 - wjandrea

24

为了扩展@piRSquared的答案,这是关于问题10的另一个版本

问题10.1

DataFrame:

d = data = {'A': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 3, 6: 5},
 'B': {0: 'a', 1: 'b', 2: 'c', 3: 'a', 4: 'b', 5: 'a', 6: 'c'}}
df = pd.DataFrame(d)

   A  B
0  1  a
1  1  b
2  1  c
3  2  a
4  2  b
5  3  a
6  5  c

输出:

   0     1     2
A
1  a     b     c
2  a     b  None
3  a  None  None
5  c  None  None

使用df.groupbypd.Series.tolist

t = df.groupby('A')['B'].apply(list)
out = pd.DataFrame(t.tolist(),index=t.index)
out
   0     1     2
A
1  a     b     c
2  a     b  None
3  a  None  None
5  c  None  None

或者更好的替代方案是使用pd.pivot_tabledf.squeeze.
t = df.pivot_table(index='A',values='B',aggfunc=list).squeeze()
out = pd.DataFrame(t.tolist(),index=t.index)

16
为了更好地理解函数pivot的工作原理,您可以查看Pandas文档中的示例。但是,如果您有重复的索引列(foo-bar)组合(例如第二个示例中的df),则pivot将失败:

pivot

pivot相反,函数pivot_table默认支持使用mean函数进行数据聚合。以下是使用sum聚合函数的示例:

pivot_table


7

调用reset_index()(以及add_suffix()

经常需要在调用pivot_tablepivot之后调用reset_index()。例如,要进行以下转换(其中一列“变成”列名)

res

您可以使用以下代码,在pivot之后,将前缀添加到新创建的列名中,并将索引(在本例中为"movies")转换回列并删除轴名称的名称:
df.pivot(index='movie', columns='week', values='sales').add_prefix('week_').reset_index().rename_axis(columns=None)

正如其他答案所提到的,"pivot" 可能指的是两种不同的操作:

  1. 非堆叠聚合(即使 groupby.agg 的结果更宽)。
  2. 重塑(类似于 Excel 中的 pivot、numpy 中的 reshape 或 R 中的 pivot_wider)

1. 聚合

pivot_tablecrosstab 只是 groupby.agg 操作的非堆叠结果。实际上,源代码 显示,在底层,以下内容是真实的:

注意:您可以使用列名列表作为indexcolumnsvalues参数。

df.groupby(rows+cols)[vals].agg(aggfuncs).unstack(cols)
# equivalently,
df.pivot_table(vals, rows, cols, aggfuncs)
1.1. crosstabpivot_table 的一种特殊情况,因此也是 groupby + unstack 的一种情况。

以下三种方式是等价的:

  • pd.crosstab(df['colA'], df['colB'])
  • df.pivot_table(index='colA', columns='colB', aggfunc='size', fill_value=0)
  • df.groupby(['colA', 'colB']).size().unstack(fill_value=0)

请注意,pd.crosstab 的开销显著较大,因此比 pivot_tablegroupby + unstack 都要慢得多。事实上,正如这里所指出的那样pivot_table 也比 groupby + unstack 慢。

2. 重塑

pivotpivot_table 的一个更为有限的版本,其目的是将长数据帧重新塑形为长数据帧。

df.set_index(rows+cols)[vals].unstack(cols)
# equivalently, 
df.pivot(index=rows, columns=cols, values=vals)
2.1. 增加行/列如问题10所示

您还可以将问题10中的见解应用于多列数据透视操作。有两种情况:

  • "长到长":通过增加索引来重塑

    case1

    代码:

    df = pd.DataFrame({'A': [1, 1, 1, 2, 2, 2], 'B': [*'xxyyzz'], 
                       'C': [*'CCDCDD'], 'E': [100, 200, 300, 400, 500, 600]})
    rows, cols, vals = ['A', 'B'], ['C'], 'E'
    
    # 使用 pivot 语法
    df1 = (
        df.assign(ix=df.groupby(rows+cols).cumcount())
        .pivot(index=[*rows, 'ix'], columns=cols, values=vals)
        .fillna(0, downcast='infer')
        .droplevel(-1).reset_index().rename_axis(columns=None)
    )
    
    # 等效地,使用 set_index + unstack 语法
    df1 = (
        df
        .set_index([*rows, df.groupby(rows+cols).cumcount(), *cols])[vals]
        .unstack(fill_value=0)
        .droplevel(-1).reset_index().rename_axis(columns=None)
    )
    
  • "长到宽":通过增加列来重塑

    case2

    代码:

    df1 = (
        df.assign(ix=df.groupby(rows+cols).cumcount())
        .pivot(index=rows, columns=[*cols, 'ix'])[vals]
        .fillna(0, downcast='infer')
    )
    df1 = df1.set_axis([f"{c[0]}_{c[1]}" for c in df1], axis=1).reset_index()
    
    # 等效地,使用 set_index + unstack 语法
    df1 = (
        df
        .set_index([*rows, df.groupby(rows+cols).cumcount(), *cols])[vals]
        .unstack([-1, *range(-2, -len(cols)-2, -1)], fill_value=0)
    )
    df1 = df1.set_axis([f"{c[0]}_{c[1]}" for c in df1], axis=1).reset_index()
    
  • 最小化使用 set_index + unstack 语法:

    case3

    代码:

    df1 = df.set_index(['A', df.groupby('A').cumcount()])['E'].unstack(fill_value=0).add_prefix('Col').reset_index()
    

1 pivot_table() aggregates the values and unstacks it. Specifically, it creates a single flat list out of index and columns, calls groupby() with this list as the grouper and aggregates using the passed aggregator methods (the default is mean). Then after aggregation, it calls unstack() by the list of columns. So internally, pivot_table = groupby + unstack. Moreover, if fill_value is passed, fillna() is called.

In other words, the method that produces pv_1 is the same as the method that produces gb_1 in the example below.

pv_1 = df.pivot_table(index=rows, columns=cols, values=vals, aggfunc=aggfuncs, fill_value=0)
# internal operation of `pivot_table()`
gb_1 = df.groupby(rows+cols)[vals].agg(aggfuncs).unstack(cols).fillna(0, downcast="infer")
pv_1.equals(gb_1) # True

2 crosstab() calls pivot_table(), i.e., crosstab = pivot_table. Specifically, it builds a DataFrame out of the passed arrays of values, filters it by the common indices and calls pivot_table(). It's more limited than pivot_table() because it only allows a one-dimensional array-like as values, unlike pivot_table() that can have multiple columns as values.


4

pandas中的pivot函数与excel中的pivot操作具有相同的功能。我们可以将数据集从长格式转换为宽格式。

enter image description here

让我们举个例子

enter image description here

我们想要将数据集转换成每个国家都变成一列,对应于国家的新确认病例作为值。我们可以使用pivot函数执行此数据操作。

enter image description here

数据集的旋转

pivot_df = pd.pivot(df, index =['Date'], columns ='Country', values =['NewConfirmed'])
## renaming the columns  
pivot_df.columns = df['Country'].sort_values().unique()

我们可以通过重置索引来将新列与索引列的级别相同。

重置索引以修改列级别

pivot_df = pivot_df.reset_index()

enter image description here


我遇到了索引日期包含重复值的错误。如何处理这个问题? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接