首页 文章

如何透视数据框

提问于
浏览
180
  • 什么是枢轴?

  • 如何转动?

  • 这是一个支点吗?

  • 长格式到宽格式?

我已经看到很多关于数据透视表的问题 . 即使他们不知道他们询问数据透视表,他们通常也是如此 . 写一个规范的问题和答案几乎是不可能的,它包含了旋转的所有方面......

......但是我要试一试 .


现有问题和答案的问题在于,问题通常集中在OP难以概括以便使用一些现有的良好答案的细微差别 . 但是,没有一个答案试图给出全面的解释(因为这是一项艰巨的任务)

看看我的一些例子google search

因此,每当有人搜索 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

问题

  • 为什么我会 ValueError: Index contains duplicate entries, cannot reshape

  • 如何调整 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
  • 如何调整 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
  • 我可以得到 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
  • 我可以一次做多个聚合吗?
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
  • 我可以聚合多个值列吗?
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
  • 可以按多列细分吗?
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
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
  • 我可以汇总列和行一起出现的频率,也就是“交叉制表”吗?
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

1 回答

  • 159

    我们首先回答第一个问题:

    问题1

    为什么我会得到ValueError:索引包含重复的条目,无法重塑

    发生这种情况是因为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.groupby pd.DataFrame.unstack

    • 做任何类型的枢轴的良好通用方法

    • 您指定将构成一个组中的透视行级别和列级别的所有列 . 您可以通过选择要聚合的其余列以及要执行聚合的功能来执行此操作 . 最后,您要 unstack 您希望在列索引中的级别 .

    • pd.DataFrame.pivot_table

    • groupby 的美化版本,具有更直观的API . 对于许多人来说,这是首选方法 . 并且是开发人员的预期方法 .

    • 指定行级别,列级别,要聚合的值以及执行聚合的函数 .

    • pd.DataFrame.set_index pd.DataFrame.unstack

    • 方便直观(包括我自己) . 无法处理重复的分组键 .

    • groupby 范例类似,我们指定最终将是行级别或列级别的所有列,并将这些列设置为索引 . 然后我们 unstack 列中我们想要的级别 . 如果其余索引级别或列级别不唯一,则此方法将失败 .

    • pd.DataFrame.pivot

    • set_index 非常相似,因为它共享重复键限制 . API也非常有限 . 它只需要 indexcolumnsvalues 的标量值 .

    • pivot_table 方法类似,我们选择要旋转的行,列和值 . 但是,我们无法聚合,如果行或列不唯一,则此方法将失败 .

    • pd.crosstab

    • 这是 pivot_table 的专用版本,其中最纯粹的形式是执行多项任务的最直观方式 .

    • pd.factorize np.bincount

    • 这是一种非常先进但非常快速的非常先进的技术 . 这不可以在任何情况下都可以使用,但是当它可以使用并且您习惯使用它时,您将获得性能奖励 .

    • pd.get_dummies pd.DataFrame.dot

    • 我用它来巧妙地执行交叉制表 .


    例子

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

    问题3

    如何调整df以使col值为列,行值为索引,val0的平均值为值,缺失值为0?

    • 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

    我可以得到除了卑鄙之外的东西,比如总和吗?

    • 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 ,我需要传递callables列表 . 另一方面, groupby.agg 能够为有限数量的特殊函数获取字符串 . groupby.agg 也会采用我们传递给其他人的相同的callables,但是利用字符串函数名称通常更有效,因为可以获得效率 .

    • 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
    

相关问题