首页 > 解决方案 > 将列中的行类别除以相同类别但不同日期的值

问题描述

我有数百万行和大约 400 个类别的数据集。每个类别都包含 2019 年、2020 年和 2021 年的每周数据。我试图通过将其每周值除以 2019 年相应的周值来标准化列值。该列包含来自不同类别的数据。

样本数据是这样的

样本数据/m25c4.png

我想得到这样的输出: 示例输出

我尝试使用 python 来做到这一点,但它需要几个小时才能运行。我的代码是这样的

for category, weeks in category_weeks_dict.items():
    for week in weeks:
        y = df.query("category== @category and Year==2019 and week==@week['value'].values
        if not y: y=np.nan
        df.loc[(df['category']==category) & (df['week']==week), 'value'] = y

该代码基本上创建了一个新列,并为每个类别和周分配了 2019 年的值。这样我就可以将 value 列除以 2019 value 列。所以我创建了一个包含每个类别及其独特周的字典 {A:[1,2,3,4], B:[1,2,3,4]...}。然后得到 y,它是每个类别和周的 2019 年值。

有没有更好的方法可以用熊猫做到这一点。我真的很感谢你的帮助。谢谢

标签: pythonpandasdataframe

解决方案


如果所有年份都有相同的类别和周数

如果数据框按照您的示例进行排序,以便每年准确重复周数和类别的数量和顺序,那么您可以对计算进行矢量化,这应该会快得多。

用于np.tile()创建一个包含 2019 年的值的数组,重复次数与您的年数一样多(示例数据中为 2 次),以便该数组与整个数据帧具有相同的长度。然后,您可以通过将值列除以该数组来计算归一化值,这将按元素进行。

import numpy as np
import pandas as pd

df = pd.DataFrame({'category': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B',
                                'C', 'C', 'C', 'C', 'A', 'A', 'A', 'A', 
                                'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C'],
                   'year': [2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019,
                            2019, 2019, 2019, 2019, 2020, 2020, 2020, 2020,
                            2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020],
                   'val': [100, 200, 300, 400, 300, 200, 500, 700,
                           450, 550, 650, 200, 100, 100, 100, 100,
                           100, 100, 100, 100, 100, 100, 100, 100],
                   'week': [1, 2, 3, 4, 1, 2, 3, 4, 
                            1, 2, 3, 4, 1, 2, 3, 4, 
                            1, 2, 3, 4, 1, 2, 3, 4]})

val_2019 = np.tile(df.val[df.year == 2019], 2)
df['normalized'] = df.val / val_2019

df
    category  year  val     week  normalized
0   A         2019  100     1     1.000000
1   A         2019  200     2     1.000000
2   A         2019  300     3     1.000000
3   A         2019  400     4     1.000000
4   B         2019  300     1     1.000000
5   B         2019  200     2     1.000000
6   B         2019  500     3     1.000000
7   B         2019  700     4     1.000000
8   C         2019  450     1     1.000000
9   C         2019  550     2     1.000000
10  C         2019  650     3     1.000000
11  C         2019  200     4     1.000000
12  A         2020  100     1     1.000000
13  A         2020  100     2     0.500000
14  A         2020  100     3     0.333333
15  A         2020  100     4     0.250000
16  B         2020  100     1     0.333333
17  B         2020  100     2     0.500000
18  B         2020  100     3     0.200000
19  B         2020  100     4     0.142857
20  C         2020  100     1     0.222222
21  C         2020  100     2     0.181818
22  C         2020  100     3     0.153846
23  C         2020  100     4     0.500000

请注意,我重命名了该values列,以避免错误,因为df.values它是一个数据框属性,包含所有列的值。

如果年份可能有不同的类别和星期

我更改了下面的示例数据来说明这种情况。无论如何,要使矢量化方法发挥作用,您必须每年调整相同的类别和周数。这可以通过将数据帧转换为宽格式来完成:

df = pd.DataFrame({'category': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 
                                'A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C',
                                'A', 'A', 'B', 'B', 'C'],
                   'year': [2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019,
                            2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020,
                            2021, 2021, 2021, 2021, 2021],
                   'val': [100, 200, 300, 400, 300, 200, 500, 700, 350,
                           200, 400, 600, 200, 300, 100, 100, 700, 700,
                           100, 200, 200, 600, 350],
                   'week': [1, 2, 3, 1, 2, 3, 1, 2, 3, 
                            1, 2, 3, 1, 2, 3, 1, 2, 3, 
                            1, 2, 1, 2, 1]})

df_wide = df.pivot(index='year', columns=['category', 'week'], values='val')
df_wide
category A                      B                       C
week     1      2       3       1       2       3       1       2       3
year                                    
2019     100.0  200.0   300.0   400.0   300.0   200.0   500.0   700.0   350.0
2020     200.0  400.0   600.0   200.0   300.0   100.0   100.0   700.0   700.0
2021     100.0  200.0   NaN     200.0   600.0   NaN     350.0   NaN     NaN

现在您可以利用 NumPy 的广播规则将每个值除以相应的 2019 值:

df_norm = df_wide / df_wide.loc[2019]
df_norm
category A                      B                       C
week     1      2       3       1       2       3       1       2       3
year                                    
2019     1.0    1.0     1.0     1.0     1.0     1.0     1.0     1.0     1.0
2020     2.0    2.0     2.0     0.5     1.0     0.5     0.2     1.0     2.0
2021     1.0    1.0     NaN     0.5     2.0     NaN     0.7     NaN     NaN

推荐阅读