python - 将列中的行类别除以相同类别但不同日期的值
问题描述
我有数百万行和大约 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 年值。
有没有更好的方法可以用熊猫做到这一点。我真的很感谢你的帮助。谢谢
解决方案
如果所有年份都有相同的类别和周数
如果数据框按照您的示例进行排序,以便每年准确重复周数和类别的数量和顺序,那么您可以对计算进行矢量化,这应该会快得多。
用于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
推荐阅读
- asp.net - 项目列表始终为空 MVC 5
- ruby-on-rails - 在嵌套事务中,如果外部事务回滚,内部事务是否会回滚?
- javascript - 由于引导程序添加了显示属性,引导程序模式的自定义动画无法关闭
- django - django.db.utils.ProgrammingError:关系“auth_permission”不存在
- c# - 使用 Unity 和 SDK Watson 进行人脸识别
- vmware-clarity - 路标内容弹出十字按钮问题
- c# - 使用 c# 进行聚合分组
- java - 如何将 int 数转换为两个数字,首先由奇数位组成,第二个由偶数位组成
- javascript - Python API 不返回 JSON 对象
- ios - 如何使用按钮获取触摸输入并将位置输出到 Swift 3 中的标签?