我可以根据元组列表在 pandas 数据框中旋转一列吗?

Can I pivot a column in a pandas dataframe based on a list of tuples?

我正在尝试按如下方式更改数据框的结构。我有一个包含历史 KPI 信息的数据集:每条记录包含日期、KPI ID、多个维度和 KPI 值。

基于 3 元组列表,我想转换此数据框,以便最终结果是现有数据框中的 2 条记录与分子和分母的组合,每条记录都来自具有相同 date/dimensions.

当前数据帧:

Date  | KPI_ID | Dimension | Value
Apr 5 | KPI_1  | Lorem     | 1
Apr 5 | KPI_2  | Lorem     | 3
Apr 5 | KPI_1  | Ipsum     | 4
Apr 5 | KPI_2  | Ipsum     | 8
Apr 5 | KPI_3  | Dolor     | 2
Apr 5 | KPI_4  | Dolor     | 2

给出 KPI_IDs 组合的三元组列表,例如 [Result_ID、KPI_Numerator、KPI_Denominator]:

[['Result_1', 'KPI_1', 'KPI_2'], ['Result_2', 'KPI_3', 'KPI_4']]

想要的结果:

Date  | Result_ID | Dimension | Numerator | Denominator
Apr 5 | Result_1  | Lorem     | 1         | 3
Apr 5 | Result_1  | Ipsum     | 4         | 8
Apr 5 | Result_2  | Dolor     | 2         | 2

我曾尝试将 df.merge 和 df.groupby 与聚合函数一起使用,但我很难理解如何才能最好地将元组列表整合到等式中。遍历数据框似乎不是答案,因为我必须手动查找具有完全相同维度的记录,我认为这是不高效的。

您可以创建一个组合(Result_ID、KPI_1、KPI_2)的dataframe,然后先根据KPI_1合并两次到原始dataframe,然后然后在KPI_2(这次也在时间和维度上匹配):

# Create combinations dataframe
cs = [['Result_1', 'KPI_1', 'KPI_2'], ['Result_2', 'KPI_3', 'KPI_4']]
df_cs = pd.DataFrame(cs, columns=['Result_ID', 'KPI_1', 'KPI_2'])

# Merge combinations dataframe to original data:
# 1. So that 'KPI_1' in combinations = 'KPI_ID' in data
# 2. So that 'KPI_2' in combinations = 'KPI_ID' in data,
#    and we get a match on ['Date', 'Dimension']
cols = ['Date', 'Result_ID', 'Dimension', 'Numerator', 'Denominator']
df_out = (df_cs
    .merge(df.rename(columns={'Value': 'Numerator'}),
           left_on='KPI_1', right_on='KPI_ID')
    .drop(columns='KPI_ID')
    .merge(df.rename(columns={'Value': 'Denominator'}),
           left_on=['Date', 'Dimension', 'KPI_2'],
           right_on=['Date', 'Dimension', 'KPI_ID'])
    .drop(columns=['KPI_ID', 'KPI_1', 'KPI_2'])
)[cols]

输出:

    Date Result_ID Dimension  Numerator  Denominator
0  Apr 5  Result_1     Lorem          1            3
1  Apr 5  Result_1     Ipsum          4            8
2  Apr 5  Result_2     Dolor          2            2

让我们在每个映射中创建一个组合,将 ResultKPIs 配对。 创建此组合的数据框,与原始数据框合并,透视,最后进行一些按摩,以获得 OP 所需的最终形式的数据。

需要注意的是 pivot 需要 indexcolumns 的独特组合;对于共享的数据,就不用担心了。

from itertools import product, chain
mapping = [['Result_1', 'KPI_1', 'KPI_2'], ['Result_2', 'KPI_3', 'KPI_4']]
maps = (product([left], [*right]) for left, *right in mapping)
maps = chain.from_iterable(maps)
maps = pd.DataFrame(maps, columns=['Result_ID', 'KPI_ID'])

maps
  Result_ID KPI_ID
0  Result_1  KPI_1
1  Result_1  KPI_2
2  Result_2  KPI_3
3  Result_2  KPI_4

(df
 .merge(maps, how='left', on='KPI_ID')
 .assign(KPI_ID = lambda df: df.KPI_ID.map({"KPI_1":"Numerator",           
                                            "KPI_2":"Denominator",
                                            "KPI_3":"Numerator",
                                            "KPI_4":"Denominator"}),
         sorter = lambda df: df.Dimension.factorize()[0])
 .pivot(['Date','Result_ID','Dimension', 'sorter'],  
         'KPI_ID', 
         'Value')
 .rename_axis(columns=None)
 .sort_values('sorter')
 .droplevel('sorter')
 .iloc[:, ::-1]
 .reset_index()
 )

    Date Result_ID Dimension  Numerator  Denominator
0  Apr 5  Result_1     Lorem          1            3
1  Apr 5  Result_1     Ipsum          4            8
2  Apr 5  Result_2     Dolor          2            2