在 pandas 中加快数据帧操作

Speed up dataframe operations in pandas

我目前正在从 R 切换到 python,想知道我是否可以加快以下数据帧操作。我有一个包含 50 万行和 17 列的销售数据集,在将它们放入仪表板之前我需要对其进行一些计算。我的数据如下所示:

location  time  product  sales
store1    2017  brandA   10
store1    2017  brandB   17 
store1    2017  brandC   15
store1    2017  brandD   19
store1    2017  catTot   86
store2    2017  brandA   8
store2    2017  brandB   23 
store2    2017  brandC   5
store2    2017  brandD   12
store2    2017  catTot   76
.         .     .         .
.         .     .         .
.         .     .         .
.         .     .         .

catTot 是我从原始数据集中获得的预聚合,它显示给定时间段内给定商店的总销售额。正如您所看到的,其他产品只是总数的一小部分,永远不会加起来到总数中,但是它们包含在总数中。由于我想在不显示所有产品的情况下反映给定位置的总销售额(由于仪表板中的性能问题),我需要将 catTot 值替换为实际上是当前值减去总和的聚合值其他产品。

目前,我通过嵌套的 for 循环进行迭代以进行更改。代码如下所示:

df['location'] = df.location.astype('category')
df['time'] = df.time.astype('category')

var_geo = []
var_time = []
for var_time in df.time.cat.categories:
    for var_geo in df.location.cat.categories:
        df_tmp = []
        fct_eur = []

        df_tmp = df[(df['location'] == var_geo) & (df['time'] == var_time)]
        fct_eur = df_tmp.iloc[len(df_tmp)-1,3] df_tmp.iloc[0:len(df_tmp)-2,3].sum()
        df.loc[(df['location'] == var_geo) & (df['time'] == var_time) & (df['product'] == 'catTot'), ['sales']] = fct_eur

如您所见,catTot 始终是屏蔽数据框中的最后一行。此操作现在每次大约需要 9 分钟,因为我有 23 个商店位置、大约 880 种产品、30 个时间段和 5 个不同的度量,这导致大约 50 万行。是否有更优雅或至少更快的方法来进行此类操作?

您可以创建一个分组键,将 "catTot" 之外的所有内容都设置为 "sales",然后 pivot_table 以聚合 sales 列,例如:

agg = df.pivot_table(
    index=['location', 'time'],
    columns=np.where(df['product'] == 'catTot', 'catTot', 'sales'),  
    values='sales', 
    aggfunc='sum'
)

这会给你:

               catTot  sales
location time
store1   2017      86     61
store2   2017      76     48

那么你可以new_total = agg['catTot'] - agg['sales']:

location  time
store1    2017    25
store2    2017    28
dtype: int64

居然有朋友提出这种方法来解决我的问题。这段代码也是他的,它构建了一个嵌套目录并将度量添加到每一行的键中,但是除了 catTot 之外的所有内容都乘以 -1。所以最后只会保留剩下的。

for row in data:
        safe_add(mapping, row[0], int(row[1]), row[2], int(row[3]))
def safe_add(mapping, store, year, brand, count):
    if not store in mapping:
        mapping[store] = {}
    if not year in mapping[store]:
        mapping[store][year] = 0
    if brand != 'catTot':
        count = count * -1
    new_count = count + mapping[store][year]
    mapping[store][year] = new_count

获得嵌套目录后,我在字典中循环一次以获取我需要将其写出的行数。我这样做是为了能够预先填充一个空的 df 并填充它。

counter=0    
for geo in mapping.keys():
    for time in mapping[store].keys():
        counter +=1
df_annex = pd.DataFrame(data=None, index=np.arange(0, counter), columns=df.columns)
for geo in mapping.keys():
    for time in mapping[store].keys():
        df_annex.iloc[counterb, 0] = geo
        .
        .

写完字典后,我简单地将 df 中的旧总数子集化,并将其与附件连接起来。这导致 7.88 秒对 9 分钟的时间。