【问题标题】:Apply arithmetic calculations on specific rows of a large dataframe对大型数据帧的特定行应用算术计算
【发布时间】:2019-04-25 18:44:17
【问题描述】:

假设我们有一个数据框 (df),其中包含大量行 (1600000X4)。此外,我们还有一个列表列表,例如:

inx = [[1,2],[4,5], [8,9,10], [15,16]]

我们需要计算inx 中每个列表的该数据框第一列和第三列的平均值以及第二列和第四列的中值。例如,对于inx 的第一个列表,我们应该对第一行和第二行执行此操作,并将所有这些行替换为包含这些计算输出的新行。最快的方法是什么?

import numpy as np
import pandas as pd

df = pd.DataFrame(np.array([[1, 2, 3, 3], [4, 5, 6, 1], [7, 8, 9, 3], [1, 1, 1, 1]]), columns=['a', 'b', 'c', 'd'])

   a  b  c  d
0  1  2  3  3
1  4  5  6  1
2  7  8  9  3
3  1  1  1  1    

inx ([1,2]) 中的第一个列表的输出将是这样的:

   a  b  c  d
0  1  2  3  3
1  5.5  6.5  7.5  2
3  1  1  1  1   

如您所见,我们不会更改第一行 (0),因为它不在主列表中。之后,我们将对[4,5] 做同样的事情。我们不会更改第 3 行中的任何内容,因为它也不在列表中。 inx 是一个大列表(超过 100000 个元素)。

【问题讨论】:

  • 你能给我们一个数据框的例子和输出的例子吗?

标签: python pandas performance dataframe bigdata


【解决方案1】:

编辑:避免循环的新方法

您可以在下面找到一种依赖 pandas 并避免循环的方法。

在生成一些与你的大小相同的假数据后,我基本上从你的 inx 行列表创建索引列表;即,你的 inx 是:

[[2,3], [5,6,7], [10,11], ...]

创建的列表是:

[[1,1], [2,2,2], [3,3],...]

之后,此列表被展平并添加到原始数据框中,以标记要操作的各种行组。 正确计算后,生成的数据框与不需要计算的原始行重新连接(在我上面的示例中,行:[0, 1, 4, 8, 9, ...])。 你会在代码中找到更多的 cmets。

在答案的最后,我还将我以前的方法留作记录。 在我的盒子上,涉及循环的旧算法需要超过 18 分钟......无法忍受! 只使用 pandas,不到半秒!!熊猫很棒!

import pandas as pd
import numpy as np
import random

# Prepare some fake data to test
data = np.random.randint(0, 9, size=(160000, 4))
df = pd.DataFrame(data, columns=['a', 'b', 'c', 'd'])

inxl = random.sample(range(1, 160000), 140000)
inxl.sort()

inx=[]
while len(inxl) > 3:
    i = random.randint(2,3)
    l = inxl[0:i]
    inx.append(l)
    inxl = inxl[i:]
inx.append(inxl)



# flatten inx (used below)
flat_inx = [item for sublist in inx for item in sublist]
# for each element (list) in inx create equivalent list (same length)
# of increasing ints. They'll be used to group corresponding rows
gr=[len(sublist) for sublist in inx]
t = list(zip(gr, range(1, len(inx)+1)))

group_list = [a*[b] for (a,b) in t]

# the groups are flatten either
flat_group_list = [item for sublist in group_list for item in sublist]

# create a new dataframe to mark rows to group retaining 
# original index for each row
df_groups = pd.DataFrame({'groups': flat_group_list}, index=flat_inx)
# and join the group dataframe to the original df
df['groups'] = df_groups
# rows not belonging to a group are marked with 0
df['groups']=df['groups'].fillna(0)

# save rows not belonging to a group for later
df_untouched = df[df['groups'] == 0]
df_untouched = df_untouched.drop('groups', axis=1)

# new dataframe containg only rows belonging to a group
df_to_operate = df[df['groups']>0]
df_to_operate = df_to_operate.assign(ind=df_to_operate.index)

# at last, we group the rows according to original inx
df_grouped = df_to_operate.groupby('groups')

# calculate mean and median
# for each group we retain the index of first row of group
df_operated =df_grouped.agg({'a' : 'mean',
                             'b' : 'median',
                             'c' : 'mean',
                             'd' : 'median',
                             'ind': 'first'})

# set correct index on dataframe
df_operated=df_operated.set_index('ind')

# finally, join the previous dataframe with saved
# dataframe of rows which don't need calcullations
df_final = df_operated.combine_first(df_untouched)

旧算法,对于这么多数据来说太慢了

这种涉及循环的算法虽然给出了正确的结果,但对于如此大量的数据需要很长时间:

import pandas as pd

df = pd.DataFrame(np.array([[1, 2, 3, 3], [4, 5, 6, 1], [7, 8, 9, 3], [1, 1, 1, 1]]), columns=['a', 'b', 'c', 'd'])

inx = [[1,2]]

for l in inx:
    means=df.iloc[l][['a', 'c']].mean()
    medians=df.iloc[l][['b', 'd']].median()
    df.iloc[l[0]]=pd.DataFrame([means, medians]).fillna(method='bfill').iloc[0]
    df.drop(index=l[1:], inplace=True)

【讨论】:

  • 我以前实现过这个结构,但由于数据框的大小,它很慢。我正在寻找一种更快的方法。
  • 我检查了关于代码的不同部分。我认为主要问题是循环。如果我们找到一种方法来删除它,它将显着提高速度。此外,inx 是一个大列表(超过 100000 个元素)。
  • 好的,@user2991243...所以你让我陷入了困境,所以我用你的大小的假数据测试了我以前的方法。我的盒子上花费的时间超过18分钟!所以我走了一条不同的路,你可以阅读上面的结果。使用 pandas 完成艰巨的工作,不到半秒!我还是不敢相信!请告诉我它是如何为你工作的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-16
  • 1970-01-01
  • 2021-12-01
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多