使用 pandas 数据框和聚合函数在 Python 中填写查找 table
Fill in lookup table in Python using pandas dataframe and aggregate function
我有一个 pandas 数据框,其中包含像今天这样不同日期的(商店、产品、价格)信息。
df = pd.DataFrame(data={'day': [1, 2, 3, 1, 2, 3, 2, 4, 5, 2, 4, 5, 2, 4, 5, 2, 4, 5],
'shop': ['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c'],
'product': ['x', 'x', 'x', 'y', 'y', 'y', 'x', 'x', 'x', 'z', 'z', 'z', 'y', 'y', 'y', 'z', 'z', 'z'],
'price': [0, 1, 2, 2, 4, 6, 1, 2, 3, 0, 1, 1, 1, 1, 0, 2, 2, 2]})
我想进行查询 table,为每个(商店、产品)组合提供最近两天的平均价格。
例如,给定商店 'a' 和产品 'y',价格分别为 2、4 和 6,因此查找 table 中的结果应该是 4 的平均值和6,也就是5.
预期结果是以下嵌套字典:
{'a': {'x': 1.5, 'y': 5.0},
'b': {'x': 2.5, 'z': 1.0},
'c': {'y': 0.5, 'z': 2.0}}
我想到了两个解决方案。
#解决方案1:嵌套for循环
lookup = {}
for categ_1 in df['shop'].unique():
df_1 = df[df['shop'] == categ_1]
lookup[categ_1] = {}
for categ_2 in df_1['product'].unique():
df_2 = df_1[df_1['product'] == categ_2]
res = df_2.iloc[-2:,:]['price'].mean()
lookup[categ_1][categ_2] = res
#解决方案 2:过滤(商店、产品)的唯一组合并对其进行迭代
lookup = {}
for i, row in df[['shop', 'product']].drop_duplicates().iterrows():
mask = ((df['shop'] == row['shop']) & (df['product'] == row['product']))
_df = df[mask]
res = _df.iloc[-2:,:]['price'].mean()
try:
lookup[row['shop']].update({row['product']: res})
except KeyError:
lookup[row['shop']] = {row['product']: res}
虽然我发现解决方案 2 更优雅,但解决方案 1 更快。
- 解决方案 1:每个循环 7.57 毫秒 ± 1.25 毫秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)
- 解决方案 2:每个循环 9.3 毫秒 ± 1.04 毫秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)
我的实际数据框包含更多的列和更多的行来迭代,所以我想,如果可能的话,避免像解决方案 1 中那样嵌套 for 循环,但也更快解决方案比解决方案 2.
如果您能改进其中一种解决方案,或找到更好的解决方案,我将很高兴
谢谢
Pandas 有一个 groupby 函数非常适合这个。
lookup = df.groupby(['shop', 'product'])
这为您提供了一个 groupby 对象。接下来的挑战是如何根据最近两天聚合您的列,因为这并不是 GroupBy 本身内置的。您可以创建一个 lambda 函数来获取每个价格列表中的最后两项并计算平均值。
df.sort_values(by='day', ascending=True, inplace=True)
avg_func = lambda x: sum(x[-2:])/2
lookup = df.groupby(['shop', 'product']).agg({'price': avg_func})
输出:
shop product
a x 1.5
y 5.0
b x 2.5
z 1.0
c y 0.5
z 2.0
我有一个 pandas 数据框,其中包含像今天这样不同日期的(商店、产品、价格)信息。
df = pd.DataFrame(data={'day': [1, 2, 3, 1, 2, 3, 2, 4, 5, 2, 4, 5, 2, 4, 5, 2, 4, 5],
'shop': ['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c'],
'product': ['x', 'x', 'x', 'y', 'y', 'y', 'x', 'x', 'x', 'z', 'z', 'z', 'y', 'y', 'y', 'z', 'z', 'z'],
'price': [0, 1, 2, 2, 4, 6, 1, 2, 3, 0, 1, 1, 1, 1, 0, 2, 2, 2]})
我想进行查询 table,为每个(商店、产品)组合提供最近两天的平均价格。
例如,给定商店 'a' 和产品 'y',价格分别为 2、4 和 6,因此查找 table 中的结果应该是 4 的平均值和6,也就是5.
预期结果是以下嵌套字典:
{'a': {'x': 1.5, 'y': 5.0},
'b': {'x': 2.5, 'z': 1.0},
'c': {'y': 0.5, 'z': 2.0}}
我想到了两个解决方案。
#解决方案1:嵌套for循环
lookup = {}
for categ_1 in df['shop'].unique():
df_1 = df[df['shop'] == categ_1]
lookup[categ_1] = {}
for categ_2 in df_1['product'].unique():
df_2 = df_1[df_1['product'] == categ_2]
res = df_2.iloc[-2:,:]['price'].mean()
lookup[categ_1][categ_2] = res
#解决方案 2:过滤(商店、产品)的唯一组合并对其进行迭代
lookup = {}
for i, row in df[['shop', 'product']].drop_duplicates().iterrows():
mask = ((df['shop'] == row['shop']) & (df['product'] == row['product']))
_df = df[mask]
res = _df.iloc[-2:,:]['price'].mean()
try:
lookup[row['shop']].update({row['product']: res})
except KeyError:
lookup[row['shop']] = {row['product']: res}
虽然我发现解决方案 2 更优雅,但解决方案 1 更快。
- 解决方案 1:每个循环 7.57 毫秒 ± 1.25 毫秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)
- 解决方案 2:每个循环 9.3 毫秒 ± 1.04 毫秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)
我的实际数据框包含更多的列和更多的行来迭代,所以我想,如果可能的话,避免像解决方案 1 中那样嵌套 for 循环,但也更快解决方案比解决方案 2.
如果您能改进其中一种解决方案,或找到更好的解决方案,我将很高兴
谢谢
Pandas 有一个 groupby 函数非常适合这个。
lookup = df.groupby(['shop', 'product'])
这为您提供了一个 groupby 对象。接下来的挑战是如何根据最近两天聚合您的列,因为这并不是 GroupBy 本身内置的。您可以创建一个 lambda 函数来获取每个价格列表中的最后两项并计算平均值。
df.sort_values(by='day', ascending=True, inplace=True)
avg_func = lambda x: sum(x[-2:])/2
lookup = df.groupby(['shop', 'product']).agg({'price': avg_func})
输出:
shop product
a x 1.5
y 5.0
b x 2.5
z 1.0
c y 0.5
z 2.0