python-3.x - 带有花式堆叠功能的 Pandas 数据透视表

标签 python-3.x pandas pivot-table

给定以下数据框:

results = pd.DataFrame({'Contractor':[1,1,0,0,0,1],
                    'President':[1,0,0,0,1,1],
                   'Item 1':[1,1,0,0,1,np.nan],
                   'Item 2':[1,0,0,1,0,1]})
results[['Contractor','President','Item 1','Item 2']]

results

    Contractor  President   Item 1  Item 2
0        1          1       1       1
1        1          0       1       0
2        0          0       0       0
3        0          0       0       1
4        0          1       1       0
5        1          1       NaN     1

这供引用项目(见下文):

    Position    Item(s)
0   Contractor  1
1   President   1,2

...我想旋转数据以产生以下结果:

    Position    Overall%
0   Contractor  100
1   President   80

...基于这个逻辑:

由于总统关心第 1 项和第 2 项,因此需要考虑 5 个数字:第 1 项中的 (1 和 1) 以及第 2 项中的 (1, 0 和 1)。各个项目的总和 为 4,跨项目计数 为 5(不计算“NaN”),即 80%。

由于承包商只关心第 1 项,因此需要考虑 2 个数字:1 和 1 - 'NaN' 不应计算在内 - (分别来自感兴趣的行)。因此,总数为 2,即 2,即 100%

提前致谢!

最佳答案

import numpy as np
import pandas as pd

results = pd.DataFrame({'Contractor':[1,1,0,0,0,1],
                    'President':[1,0,0,0,1,1],
                   'Item 1':[1,1,0,0,1,np.nan],
                   'Item 2':[1,0,0,1,0,1]})
reference =  pd.DataFrame({'Position':['Contractor','President'],
                           'Item(s)':[(1,), (1,2)]})

longref = pd.DataFrame([('Item {}'.format(item), row['Position']) 
                        for index, row in reference.iterrows() 
                        for item in row['Item(s)']], columns=['Item', 'Position'])
melted = pd.melt(results, id_vars=['Item 1','Item 2'], var_name='Position')
melted = melted.loc[melted['value']==1]
melted = pd.melt(melted, id_vars=['Position'], 
                 value_vars=['Item 1','Item 2'], var_name='Item')
merged = pd.merge(longref, melted, how='left')
grouped = merged.groupby(['Position'])
result = (grouped['value'].sum() / grouped['value'].count())*100
result = result.rename('Overall%').reset_index()
print(result)

产量

     Position  Overall%
0  Contractor     100.0
1   President      80.0

说明:有一篇文章,作者:Hadley Wickham ( PDF ) 提出优点 使数据“整洁”。主要原则是每一行都应该 代表一个“观察”,每列代表一些因素或变量。

事实证明,您经常需要使用工具来表达您的计算 一旦数据整洁,就会很自然地就位。 这个问题的难度很大程度上来自于数据不整齐。

考虑结果:

In [405]: results
Out[405]: 
   Contractor  Item 1  Item 2  President
0           1     1.0       1          1
1           1     1.0       0          0
2           0     0.0       0          0
3           0     0.0       1          0
4           0     1.0       0          1
5           1     NaN       1          1

与其为 ContractorPresident 设置单独的列,最好有一个名为 Position 的列,因为 Position 是变量,每个观察或行都可以有一个Position 值——ContractorPresident。 同样,Item 1Item 2 应合并为单列 Item:

In [416]: melted
Out[416]: 
      Position    Item  value
0   Contractor  Item 1    1.0
1   Contractor  Item 1    1.0
2   Contractor  Item 1    NaN
3    President  Item 1    1.0
4    President  Item 1    1.0
5    President  Item 1    NaN
6   Contractor  Item 2    1.0
7   Contractor  Item 2    0.0
8   Contractor  Item 2    1.0
9    President  Item 2    1.0
10   President  Item 2    0.0
11   President  Item 2    1.0

melted 包含与 results 相同的信息,但格式简洁。 value 列包含 results[['Item 1', 'Item 2']] 中的值。每行对应一个“观察”,其中 results['Contractor'] 或 result['President']` 等于 1,因为计算逻辑只需要这些值。

同样,代替

In [407]: reference
Out[407]: 
  Item(s)    Position
0    (1,)  Contractor
1  (1, 2)   President

拥有一个列为 ItemPosition 的 DataFrame 会更整洁:

In [408]: longref
Out[408]: 
     Item    Position
0  Item 1  Contractor
1  Item 1   President
2  Item 2   President

一旦您获得了 meltedlongref 形式的整洁数据版本, 计算所需的结果相当简单:

merged = pd.merge(longref, melted, how='left')
#      Item    Position  value
# 0  Item 1  Contractor    1.0
# 1  Item 1  Contractor    1.0
# 2  Item 1  Contractor    NaN
# 3  Item 1   President    1.0
# 4  Item 1   President    1.0
# 5  Item 1   President    NaN
# 6  Item 2   President    1.0
# 7  Item 2   President    0.0
# 8  Item 2   President    1.0

grouped = merged.groupby(['Position'])
result = (grouped['value'].sum() / grouped['value'].count())*100
result = result.rename('Overall%').reset_index()

如何整理引用以生成longref:

只需迭代 reference 的行,并为每一行迭代项目元组以构建新的 DataFrame,longref:

longref = pd.DataFrame([('Item {}'.format(item), row['Position']) 
                        for index, row in reference.iterrows() 
                        for item in row['Item(s)']], columns=['Item', 'Position'])

如何整理结果以使融化:

可以通过两次调用 pd.melt 来完成。 pd.melt 将“宽”格式转换为“长”格式 DataFrame。它可以将多个列合并为单个列。例如,要将承包商和总裁列合并为单个职位列,您可以使用:

melted = pd.melt(results, id_vars=['Item 1','Item 2'], var_name='Position')
# we only care about rows where Contractor or President value was 1. So use .loc to select those rows.
melted = melted.loc[melted['value']==1]
#     Item 1  Item 2    Position  value
# 0      1.0       1  Contractor      1
# 1      1.0       0  Contractor      1
# 5      NaN       1  Contractor      1
# 6      1.0       1   President      1
# 10     1.0       0   President      1
# 11     NaN       1   President      1

类似地,要将 Item 1Item 2 列合并为单个 Item 列,请使用:

melted = pd.melt(melted, id_vars=['Position'], 
                 value_vars=['Item 1','Item 2'], var_name='Item')
#       Position    Item  value
# 0   Contractor  Item 1    1.0
# 1   Contractor  Item 1    1.0
# 2   Contractor  Item 1    NaN
# 3    President  Item 1    1.0
# 4    President  Item 1    1.0
# 5    President  Item 1    NaN
# 6   Contractor  Item 2    1.0
# 7   Contractor  Item 2    0.0
# 8   Contractor  Item 2    1.0
# 9    President  Item 2    1.0
# 10   President  Item 2    0.0
# 11   President  Item 2    1.0

关于python-3.x - 带有花式堆叠功能的 Pandas 数据透视表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37245338/

相关文章:

python - 为什么数据透视表返回 Int64 类型错误?

python - 在抛出之前操作异常的堆栈跟踪 (Python)

python - 将聚合函数应用于 Pandas 分层索引中的多个索引片的系统方法

Pandas:使用 SimpleImputer 将数据帧转换为一系列?

mysql - 使用 SQL 建表

laravel - 如何使用Eloquent过滤数据透视表?

python - 这个将 'string' 视为 'list' 的回文代码如何工作?

python - 如何释放python删除对象的内存?

python-3.x - 如何绘制多类数据的 ROC 曲线并从混淆矩阵测量 MAUC

python - Pandas 使用其他不规则时间列表对不规则时间序列进行重新采样和插值