我如何透视一个数据框架?

  • 什么是主元?
  • 我如何旋转?
  • 这是枢轴吗?
  • 长幅转宽幅?

我见过很多关于数据透视表的问题。即使他们不知道他们问的是关于数据透视表的问题,他们通常都知道。几乎不可能写出一个涵盖了旋转的所有方面的规范问题和答案……

... 但我还是要试一试。


现有问题和答案的问题是,问题往往集中在一个细微差别上,而OP为了使用一些现有的好答案而难以概括。然而,没有一个答案试图给出一个全面的解释(因为这是一个艰巨的任务)

看看我的< >强谷歌搜索< / >强中的几个例子

  1. 如何在熊猫中透视一个数据框架?< / >
  • 很好的问题和回答。但答案只是回答了具体的问题,几乎没有解释。
  1. 熊猫数据透视表到数据帧
  • 在这个问题中,OP与主元的输出有关。也就是这些列的样子。OP想让它看起来像r,这对熊猫用户没有多大帮助。
  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

问题(s)

  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. < p >或

     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. 我如何转换一个数据帧从长到宽的旋转上只有两列?考虑到,

    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
    
58339 次浏览

问题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()

下面是我们可以用来支点的习语列表

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

    • 对于做任何类型的支点都是很好的通用方法
    • 通过指定将构成一个组中的枢轴行级别和列级别的所有列。接下来,选择要聚合的其余列和要执行聚合的函数。最后,您unstack您希望在列索引中的级别。
  2. < p > # EYZ1

    • groupby的美化版本,具有更直观的API。对于许多人来说,这是首选的方法。这是开发人员的预期方法。
    • 指定行级别、列级别、要聚合的值和执行聚合的函数。
  3. pd.DataFrame.set_index + pd.DataFrame.unstack

    • 方便和直观的一些(包括我自己)。不能处理重复的分组键。
    • groupby范例类似,我们指定最终将成为行级或列级的所有列,并将它们设置为索引。然后我们在列中unstack我们想要的级别。如果剩下的索引级别或列级别不是唯一的,则此方法将失败。
  4. < p > # EYZ1

    • set_index非常相似,因为它共享重复键的限制。API也非常有限。它只接受indexcolumnsvalues的标量值。
    • pivot_table方法类似,我们选择要以其为枢轴的行、列和值。但是,我们不能聚合,如果行或列不是唯一的,这个方法就会失败。
  5. < p > # EYZ1

    • 这是pivot_table的专门化版本,以最纯粹的形式是执行几个任务的最直观的方式。
  6. pd.factorize + np.bincount

    • 这是一种非常先进的技术,非常晦涩,但非常快速。它不能在所有情况下使用,但是当它可以使用并且您使用它感到舒适时,您将获得性能奖励。
  7. pd.get_dummies + pd.DataFrame.dot

    • 我用它来巧妙地执行交叉表格。

例子

我要为每个后续的答案和问题做的是使用pd.DataFrame.pivot_table来回答它。然后,我将提供执行相同任务的替代方案。

问题3

我如何枢轴df,使col值是列,row值是索引,val0的平均值是值,而缺失的值是0?

  • < p > # EYZ1

    • 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
    
  • < p > # EYZ1

    df.groupby(['row', 'col'])['val0'].mean().unstack(fill_value=0)
    
  • < p > # EYZ1

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

问题4

我能得到mean以外的东西吗,比如sum?

  • < p > # EYZ1

    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
    
  • < p > # EYZ1

    df.groupby(['row', 'col'])['val0'].sum().unstack(fill_value=0)
    
  • < p > # EYZ1

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

问题5

我能一次做更多的聚合吗?

注意,对于pivot_tablecrosstab,我需要传递可调用对象列表。另一方面,groupby.agg能够为有限数量的特殊函数接受字符串。groupby.agg也将获得我们传递给其他函数的相同的可调用对象,但利用字符串函数名通常更有效,因为可以获得效率。

  • < p > # EYZ1

    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
    
  • < p > # EYZ1

    df.groupby(['row', 'col'])['val0'].agg(['size', 'mean']).unstack(fill_value=0)
    
  • < p > # EYZ1

    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
    
  • < p > # EYZ1

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

问题7

可以细分多列吗?

  • < p > # EYZ1

    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
    
  • < p > # EYZ1

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

问题8

可以细分多列吗?

  • < p > # EYZ1

    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
    
  • < p > # EYZ1

    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

我是否可以汇总列和行同时出现的频率,即“交叉列表”?

  • < p > # EYZ1

    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
    
  • < p > # EYZ1

    df.groupby(['row', 'col'])['val0'].size().unstack(fill_value=0)
    
  • < p > # EYZ1

    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
    
  • < p > # EYZ1

    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

我如何通过只旋转两个数据帧从长到宽转换 列?< / p >

  • < p > # EYZ1

    第一步是为每一行分配一个数字——这个数字将是该值在枢轴化结果中的行索引。这是使用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
    
  • < p > # EYZ1

    虽然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)

其他# EYZ0

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

@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
< p >或 使用pd.pivot_tabledf.squeeze.

是一个更好的选择
t = df.pivot_table(index='A',values='B',aggfunc=list).squeeze()
out = pd.DataFrame(t.tolist(),index=t.index)

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

pivot

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

pivot_table

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

rows, cols, vals, aggfuncs = ['row', 'key'], ['col', 'item'], ['val0', 'val1'], ['mean', 'sum']


df.groupby(rows+cols)[vals].agg(aggfuncs).unstack(cols)
# equivalently,
df.pivot_table(vals, rows, cols, aggfuncs)




df.set_index(rows+cols)[vals].unstack(cols)
# equivalently,
df.pivot(rows, cols, vals)

你也可以将问题10中的见解应用到多列枢轴运算中。只需将辅助索引从groupby().cumcount()附加到rowscols,这取决于你想要的结果(将它附加到rows会使结果“长”,将它附加到cols会使结果“宽”)。此外,调用droplevel().reset_index()可以修复多余和重复的索引问题。

# for "long" result
df.assign(ix=df.groupby(rows+cols).cumcount()).pivot(rows+['ix'], cols, vals).droplevel(-1).reset_index()


# for "wide" result
df.assign(ix=df.groupby(rows+cols).cumcount()).pivot(rows, cols+['ix'], vals).droplevel(-1, axis=1).reset_index()

例如,下面的代码是无效的。

df = pd.DataFrame({'A': [1, 1, 2], 'B': ['a', 'a', 'b'], 'C': range(3)})
df.pivot('A','B','C')

但以下工作:

# long
(
df.assign(ix=df.groupby(['A','B']).cumcount())
.pivot(['A','ix'], 'B', 'C')
.droplevel(-1).reset_index()
)


B  A    a    b
0  1  0.0  NaN
1  1  1.0  NaN
2  2  NaN  2.0






# wide
(
df.assign(ix=df.groupby(['A','B']).cumcount())
.pivot('A', ['B', 'ix'], 'C')
.droplevel(-1, axis=1).reset_index()
)


B  A    a    a    b
0  1  0.0  1.0  NaN
1  2  NaN  NaN  2.0

pivot_table()aggfunc的结果是聚合数据,这与groupby.agg()非常相似。pivot()只是简单地重塑和/或堆叠数据(让人想起numpy的重塑和堆叠方法),所以很自然地,它与它们的熊猫表亲unstack()stack()有关。

事实上,如果我们在内部检查源代码,每个方法对都是相同的。

  1. 数据透视表= groupby + unstack
  2. Pivot = set_index + unstack
  3. 交叉表=数据透视表

使用OP中的设置:

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'))


rows, cols, vals, aggfuncs = ['row', 'key'], ['col', 'val1'], ['val0'], ['mean', 'sum']
  1. pivot_table()聚合值并将其解堆栈。具体来说,它从索引和列中创建一个单一的平面列表,使用该列表作为石斑调用groupby(),并使用传递的聚合器方法进行聚合(默认为mean)。然后在聚合之后,它根据列列表调用unstack()。所以在内部,数据透视表= groupby + unstack。此外,如果传递了fill_value,则调用fillna()

    换句话说,生成pv_1的方法与下面示例中生成gb_1的方法相同。

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
  1. pivot()从作为索引和列传递的列值创建一个MultiIndex,构建一个MultiIndex DataFrame并通过列列表调用unstack()。所以内部,Pivot = set_index + unstack

    换句话说,下面这些都是正确的:

# if the entire df needs to be pivoted
pv_2 = df.pivot(index=rows, columns=cols)
# internal operation of `pivot()`
su_2 = df.set_index(rows+cols).unstack(cols)
pv_2.equals(su_2) # True


# if only subset of df.columns need to be considered for pivot, specify so
pv_3 = df.pivot(index=rows, columns=cols, values=vals)
su_3 = df.set_index(rows+cols)[vals].unstack(cols)
pv_3.equals(su_3) # True


# this is the precise method used internally (building a new DF seems to be faster than set_index of an existing one)
pv_4 = df.pivot(index=rows, columns=cols, values=vals)
su_4 = pd.DataFrame(df[vals].values, index=pd.MultiIndex.from_arrays([df[c] for c in rows+cols]), columns=vals).unstack(cols)
pv_4.equals(su_4) # True
  1. crosstab()调用pivot_table(),即交叉表=数据透视表。具体来说,它从传递的值数组中构建一个DataFrame,通过公共索引过滤它并调用pivot_table()。它比pivot_table()更有限制,因为它只允许像values这样的一维数组,而不像pivot_table()那样可以有多个列。

    换句话说,以下是正确的。

indexes, columns, values = [df[r] for r in rows], [df[c] for c in cols], next(df[v] for v in vals)
# crosstab
ct_5 = pd.crosstab(indexes, columns, values, aggfunc=aggfuncs)
# internal operation (abbreviated)
from functools import reduce
data = pd.DataFrame({f'row_{i}': r for i, r in enumerate(indexes)} | {f'col_{i}': c for i, c in enumerate(columns)} | {'v': values},
index = reduce(lambda x, y: x.intersection(y.index), indexes[1:]+columns, indexes[0].index)
)
pv_5 = data.pivot_table('v', [k for k in data if k[:4]=='row_'], [k for k in data if k[:4]=='col_'], aggfuncs)
ct_5.equals(pv_5) # True

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

enter image description here

我们来举个例子

enter image description here

我们希望将数据集转换为一种形式,使每个国家成为一列,新确诊病例作为对应国家的值。我们可以使用枢轴函数来执行这个数据操作。

enter image description here

Pivot数据集

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

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

重置索引以修改列级别

Pivot_df = Pivot_df .reset_index()

enter image description here