使用 pandas 制作复杂的数据透视表

问题描述 投票:0回答:2

我有一个这样的数据框

df = pd.DataFrame(data={'rem': [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3], 'rp': [22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22.,
       22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22.,
       22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 27., 27., 27.,
       27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27.,
       27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27.,
       27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27.,
       27., 27., 27., 27., 27., 27., 27., 27., 27., 22., 22., 22., 22.,
       22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 22.,
       22., 22., 22., 22., 22., 22., 22., 22., 22., 22., 27., 27., 27.,
       27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27.,
       27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27.,
       27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27., 27.], 'road': ['92', '92', '92', '92', '01', '01', '01', '01', '17', '24', '24',
       '28', '28', '28', '51', '51', '51', '58', '58', '61', '61', '63',
       '76', '80', '80', '80', '83', '83', '83', '88', '88', '88', '92',
       '92', '94', '94', '01', '01', '01', '01', '01', '17', '17', '17',
       '17', '24', '24', '24', '24', '24', '24', '28', '28', '28', '28',
       '51', '51', '51', '58', '58', '58', '58', '58', '58', '61', '63',
       '76', '76', '76', '80', '80', '83', '83', '83', '83', '83', '83',
       '83', '88', '88', '88', '88', '88', '92', '92', '92', '94', '94',
       '01', '01', '01', '17', '24', '24', '28', '51', '51', '51', '58',
       '58', '63', '63', '80', '80', '83', '83', '88', '88', '88', '92',
       '92', '92', '94', '94', '01', '01', '01', '01', '01', '17', '17',
       '24', '24', '24', '24', '24', '28', '28', '28', '51', '51', '58',
       '58', '58', '58', '58', '58', '58', '61', '63', '76', '76', '76',
       '80', '83', '83', '83', '83', '83', '88', '88', '88', '88', '92',
       '92', '92'], 'vrp': [ 647.,  647.,  651.,  651.,  323.,  341.,  344., 2566.,  336.,
        371.,  415.,  385.,  391.,  728.,  512.,  561.,  574.,  488.,
       1785.,  676.,  682.,  712.,  588.,  595.,  598.,  600.,  605.,
        621.,  628.,  634.,  635., 2571.,  231.,  653.,  640.,  643.,
        319.,  323.,  341.,  345., 2566.,  180.,  334.,  400., 2048.,
        371.,  414.,  415.,  420.,  423., 1393.,  382.,  385.,  388.,
        391.,  514.,  561.,  562.,  301.,  481.,  492.,  496.,  540.,
       1785.,  682.,  712.,  582.,  585.,  588.,  595.,  598.,  605.,
        611.,  625.,  626.,  628., 1566., 2586.,  443.,  634.,  635.,
       2571., 2603.,  231.,  650., 1314.,  643.,  640.,  319.,  323.,
        341.,  400.,  414.,  415.,  382.,  512.,  561.,  574.,  301.,
       1785.,  311.,  712.,  595.,  598.,  625.,  628.,  634.,  635.,
       2571.,  231.,  650., 1314.,  640.,  643.,  319.,  323.,  341.,
        345., 2566.,  180., 1429.,  414.,  415.,  420.,  423., 1393.,
        382.,  388.,  391.,  561.,  562.,  301.,  480.,  481.,  488.,
        492.,  540., 1785.,  682.,  311.,  582.,  585.,  588.,  600.,
        611.,  621.,  625.,  628., 1566.,  443.,  634.,  635., 2571.,
        231.,  650., 1314.], 'naim': [2., 2., 1., 1., 6., 2., 3., 4., 2., 1., 3., 1., 2., 1., 6., 2., 6.,
       3., 6., 1., 2., 3., 1., 3., 3., 2., 1., 1., 3., 3., 1., 6., 3., 1.,
       1., 2., 3., 6., 2., 2., 4., 6., 3., 3., 6., 1., 3., 3., 6., 3., 6.,
       2., 1., 2., 2., 2., 2., 2., 2., 2., 2., 3., 3., 6., 2., 3., 3., 3.,
       1., 3., 3., 1., 3., 3., 1., 3., 6., 4., 2., 3., 1., 6., 6., 3., 3.,
       4., 2., 1., 3., 6., 2., 3., 3., 3., 2., 6., 2., 6., 2., 6., 6., 3.,
       3., 3., 3., 3., 3., 1., 6., 3., 3., 4., 1., 2., 3., 6., 2., 2., 4.,
       6., 4., 3., 3., 6., 3., 6., 2., 2., 2., 2., 2., 2., 3., 2., 3., 2.,
       3., 6., 2., 6., 3., 3., 1., 2., 3., 1., 3., 3., 6., 2., 3., 1., 6.,
       3., 3., 4.], 'type': ['way', 'arrived', 'way', 'arrived', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'way', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected', 'rejected',
       'rejected', 'rejected', 'rejected'], 'overall': [  1.,   1.,   1.,   5.,   1.,   1.,   2.,  19.,   9.,   5.,   4.,
         3.,   1.,   2.,   5.,  14.,  15.,   6.,   2.,   1.,   1.,  10.,
         1.,   1.,  10.,  20.,   5.,  10.,  14.,   6.,  10.,   5.,   1.,
        10.,   3.,   5.,   8.,   4.,   8.,  18.,  30.,  20.,   1.,  13.,
        10.,  15.,  20.,   3.,  40.,   1.,  45.,  20.,  15.,  20.,  16.,
         3.,   5.,  10.,   9.,  73., 105.,  20.,  15.,  30.,  11.,  10.,
        40.,   9.,  13.,  17.,   5.,  30.,  10.,  15.,   4.,  25.,  10.,
        50.,  10.,   5.,   2.,   5.,  40.,  15.,  15.,  15.,   2.,   3.,
         7.,  10.,   1.,   1.,   5.,   4.,   6.,   8.,   2.,  15.,   4.,
         3.,  12.,   1.,  12.,  10.,  15.,   7.,   5.,   2.,   3.,   1.,
         1.,  11.,   2.,   5.,   7.,  15.,   5.,   2.,   1.,  10.,   4.,
        10.,   4.,  10.,   5.,   5.,  20.,   6.,  10.,   3.,   5.,   4.,
        13.,  12.,   3.,  40.,   5.,  15.,   3.,   3.,  10.,  10.,   3.,
         9.,  10.,  10.,  15.,   4.,  10.,   5.,  15.,   1.,   5.,   7.,
         9.,   4.]}) 

# first pivot 
df_2_res_opti = pd.pivot_table(
    df.query("type == 'rejected'").rename(columns={'overall': 'overall_1'}),
    values=["rem", "rp", "overall_1"],
    index=["road", "vrp", "naim"],
    columns=["rem", "rp"],
    aggfunc={"overall_1": np.sum},
)

# second pivot
df_2_res_in_vrp = pd.pivot_table(
    df.query("type == 'arrived'").rename(columns={'overall': 'overall_2'}),
    values=["rem", "rp", "overall_2"],
    index=["road", "vrp", "naim"],
    columns=["rem", "rp"],
    aggfunc={"overall_2": np.sum},
)

# third pivot
df_2_res_to_vrp = pd.pivot_table(
    df.query("type == 'way'").rename(columns={'overall': 'overall_3'}),
    values=["rem", "rp", "overall_3"],
    index=["road", "vrp", "naim"],
    columns=["rem", "rp"],
    aggfunc={"overall_3": np.sum},
)

# fourth pivot
df_2_res_overall = pd.pivot_table(
    df.rename(columns={'overall': 'overall_4'}),
    values=["rem", "rp", "overall_4"],
    index=["road", "vrp", "naim"],
    columns=["rem", "rp"],
    aggfunc={"overall_4": np.sum},
)

# overall pivot
df_new = pd.concat([df_2_res_in_vrp, df_2_res_to_vrp, df_2_res_opti, df_2_res_overall], axis=0)

但是我想在每个 overal_* 内添加一列,这将是 rem 级别每行的总和,即对于 overall_1 它将是新列 2_sum,即 rem 2 和3_sumrem 3 的总和,并且对于每个 overall_* 都相同。

不知道说清楚了没有。也可能可以用一行代码左右编写这个数据透视表,而不是像我那样连接多个数据帧。

python pandas pivot
2个回答
0
投票

不幸的是,除了添加更多

pd.pivot_tables
并将它们连接起来之外,我没有找到一种简单的方法来做到这一点。 Pd.pivot_tables 确实有一个
margins
的参数,它给出行总和,但与您的要求的结构不完全相同。如果您找到更好的方法,请告诉我。

请参阅下面的代码,该代码仅针对您的第一个数据点(即“overall_1”)执行此操作,您可以将其扩展到其他数据点:

df2 = pd.pivot_table(
    df,
    values=["overall"],
    index=["road", "vrp", "naim"],
    columns=["rem"],
    aggfunc={"overall": np.sum},
) # pivot just on "rem" columns

# rename multi-index columns names
df2.columns = df2.columns.set_levels(['rem_'+str(x) for x in df2.columns.levels[1]], level=1) # rename levl1
df2.columns =  df2.columns.set_levels(['overall_1'], level=0) # rename level 0
df2.columns = pd.MultiIndex.from_tuples([x + ("",) for x in df2.columns], names=list(df2.columns.names) + [df_2_res_opti.columns.names[-1]]) # add empty level for "rp"

# combine to original and sort
df_2_res_opti[df2.columns] = df2 # "merge" operation
idx = np.array([float(x[1][-1] + ".5") if isinstance(x[1], str) else x[1] for x in df_2_res_opti.columns]).argsort() # hacky way to sort
df_2_res_opti[df_2_res_opti.columns[idx]]
df_2_res_opti

输出:


0
投票

结果是这样的?:

代码:

# total pivot
df_2_res_overall = pd.pivot_table(
    df,
    values=["rem", "rp", "overall"],
    index=["road", "vrp", "naim"],
    columns=["rem", "rp"],
    aggfunc={"overall": np.sum},
)
df_2_res_overall[("overall", "", "")] = df_2_res_overall.sum(1)


# add type group index
dfgroup = (
    pd.pivot_table(
        df,
        values=["rem", "rp", "overall"],
        index=["road", "vrp", "naim", "type"],
        columns=["rem", "rp"],
        aggfunc={"overall": np.sum},
    )
    .unstack(-1)
    .swaplevel(0, axis=1)
    .droplevel(-1, axis=1)
)
dfgroup.columns = dfgroup.columns.sort_values()
df_has_sum = dfgroup.groupby(level=0, axis=1).apply(
    lambda x: x.assign(total=x.sum(1)).droplevel(0, axis=1)
)

result = df_has_sum.join(df_2_res_overall)
© www.soinside.com 2019 - 2024. All rights reserved.