如何获取分类变量中类别的各种组合并同时对其进行聚合?

2024-01-10

我试图获取三列中数据的各种组合,在这样做的同时,我还想聚合(求和)这些值。

我的数据如下所示,下面是我的示例输出:

Dim1    Dim2    Dim3    Spend
A       X       Z       100
A       Y       Z       200
B       X       Z       300
B       Y       Z       400

示例输出:

Dim 1   Dim 2   Dim 3   Spend
A       NaN     NaN     300
A       X       NaN     100
A       Y       NaN     200
A       NaN     Z       300
B       NaN     NaN     700
B       X       NaN     300
B       Y       NaN     400
B       NaN     Z       700
NaN     X       Z       400
NaN     Y       Z       600
NaN     NaN     Z       1000
NaN     X       NaN     400
NaN     Y       NaN     600
A       X       Z       100
A       Y       Z       200
B       X       Z       300
B       Y       Z       400

Dim1, Dim2, Dim3是分类变量并且Spend是一个值/度量。我们需要找到总共Spend关于分类变量的所有可能组合,这部分我可以使用来实现itertools.combinations()。现在,不仅对于三列,我们还可以获得任意数量的此类变量的组合,例如 Dim1、Dim2、Dim3 .. Dim 30 等。

我的问题是我无法聚合相同的内容,例如在第 12 行中,对于Spend类别价值Z,我们正在执行sum()Z 出现在主数据中的所有值的总和,因此值为 1000。对于聚合,我们如何实现这一目标?


可重现的数据:

data = pd.DataFrame({'Dim1': ['A', 'A', 'B', 'B'],
 'Dim2': ['X', 'Y', 'X', 'Y'],
 'Dim3': ['Z', 'Z', 'Z', 'Z'],
 'Spend': [100, 200, 300, 400]})

Digest

你有正确的使用想法itertools.combinations()。进一步的关键步骤:

  1. Apply itertools.combinations()对每个可能的维数进行求和(从1 to n_dim-1). i.e. itertools.combinations(range(1, 1+n_dim), i), for i in range(1, 1+n_dim).
  2. Use df.groupby(by=column_combinations).sum()自动从类的组合中获取结果。

Code

该程序由 3 个逻辑部分组成。

  1. 从每个维度按类聚合。这部分基本上与您所做的相同,但是通过DFS方法重新设计以减少正在处理的数据总量。当需要处理数百万行时,这非常有用。后续步骤也是基于此中间数据​​集而不是原始数据集来计算的。
  2. 一个生成器,用于循环摘要 1 中提到的维度组合,并且无需显式枚举。
  3. 执行摘要 2 中提到的分组计算并输出结果数据帧列表,这些结果数据帧可以在程序末尾连接。

Caution:请务必在生产使用中测试性能和内存问题。

import pandas as pd
import numpy as np
import itertools

df = pd.DataFrame(
    {'Dim1': ['A', 'A', 'B', 'B'],
     'Dim2': ['X', 'Y', 'X', 'Y'],
     'Dim3': ['Z', 'Z', 'Z', 'Z'],
     'Spend': [100, 200, 300, 400]
     }
)

# constants: column names and dimensions
n_dim = 3
dim_cols = [f"Dim{i}" for i in range(1, n_dim + 1)]
cols = dim_cols + ["Spend"]


# 1. compute sums with every dimension
def dfs(df, ls_out, dim_now=1, ls_classes=[]):

    # termination condition (every dimension has been traversed)
    if dim_now == n_dim + 1:
        # perform aggregation
        sum = df["Spend"].sum()
        ls_classes.append(sum)
        ls_out.append(ls_classes)
        return

    # proceed
    col = f"Dim{dim_now}"

    # get categories
    classes = df[col].unique()
    classes.sort()

    for c in classes:
        # recurse next dimension with subset data
        dfs(df[df[col] == c], ls_out,
            dim_now=dim_now + 1,
            ls_classes=ls_classes + [c])

ls_out = []  # the output container
dfs(df, ls_out)
# convert to dataframe
df_every_dim = pd.DataFrame(data=ls_out, columns=df.columns)
del ls_out
print(df_every_dim)


# 2. generate combinations of groupby-dimensions
def multinomial_combinations(n_dim):
    for i in range(1, 1+n_dim):
        for tup in itertools.combinations(range(1, 1+n_dim), i):
            yield tup

print("Check multinomial_combinations(4):")
for i in multinomial_combinations(4):
    print(i)

# 3. Sum based on from df_every_dim
def aggr_by_dims(df, by_dims):

    # guard
    if not (0 < len(by_dims) < n_dim):
        raise ValueError(f"Wrong n_dim={n_dim}, len(by_dims)={len(by_dims)}")

    # by-columns
    by_cols = [f"Dim{i}" for i in by_dims]

    # groupby-sum
    df_grouped = df.groupby(by=by_cols).sum().reset_index()

    # create none-columns (cannot be empty here)
    arr = np.ones(n_dim+1, dtype=int)
    arr[list(by_dims)] = 0
    for i in range(1, 1+n_dim):
        if arr[i] == 1:
            df_grouped[f"Dim{i}"] = None  # or np.nan as you wish

    # reorder columns
    return df_grouped[cols]

print("\nCheck aggr_by_dims(df_every_dim, [1, 3]):")
print(aggr_by_dims(df_every_dim, [1, 3]))

# combine 2. and 3.
ls = []
for by_dims in multinomial_combinations(n_dim):
    if len(by_dims) < n_dim:
        df_grouped = aggr_by_dims(df_every_dim, by_dims)
        ls.append(df_grouped)

# no none-dimensions
ls.append(df_every_dim)

# final result
df_ans = pd.concat(ls, axis=0)
df_ans.reset_index(drop=True, inplace=True)
print(df_ans)

Output

(省略中间输出)

    Dim1  Dim2  Dim3  Spend
0      A  None  None    300
1      B  None  None    700
2   None     X  None    400
3   None     Y  None    600
4   None  None     Z   1000
5      A     X  None    100
6      A     Y  None    200
7      B     X  None    300
8      B     Y  None    400
9      A  None     Z    300
10     B  None     Z    700
11  None     X     Z    400
12  None     Y     Z    600
13     A     X     Z    100
14     A     Y     Z    200
15     B     X     Z    300
16     B     Y     Z    400
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何获取分类变量中类别的各种组合并同时对其进行聚合? 的相关文章

随机推荐