【问题标题】:how to optimize time of this python script Pandas?如何优化这个 python 脚本 Pandas 的时间?
【发布时间】:2021-08-27 09:36:24
【问题描述】:

我有需要优化的脚本。示例数据在此处添加data

我尝试了几件事,将 groupby 更改为使用排序值。我已经尝试过更快速地快速申请,但它需要更多时间。

df_loop = pd.read_excel("data.xlsx")

df_loop.index = df_loop["Destination"]

uni_list = df_loop["Destination"].unique()

def get_custcon(x):
    dfa = df_loop[(df_loop.index.isin(x))]
    dfa.sort_values\
    (by = ["Source","Time"],ascending=True).drop_duplicates('Source'\
                                            ,keep='first',inplace = True)
    return  [x,dfa["con"].sum()/dfa["Options"].sum()]



def get_optimisation(site):
    list_site = []
    for si in range(site):
        s = "step_" + str(si) 
        list_site.append(s)
    list_site = ["random_combination"] + list_site + ["Cust"] 
    test_data = pd.DataFrame(columns = list_site)
    Iteration = 1 #how many iteration do you want to run 
    for it in range(Iteration):
        test_list = []
        random_com = tuple(random.sample(set(df_loop["Source"]\
                                              .unique()),site)) ### random combination
        test_list.append(random_com)

        for i in range(site):
            li = list(range(site))
            li.remove(i)
            col_dict = {}
            for k in li:
                j = "site_" + str(k)
                if j not in col_dict:
                    col_dict[j] = [random_com[k]]* 5
            df_com = pd.DataFrame(col_dict)
                
            df_com["site_"+str(i)] = uni_list
            df_com["res"] = df_com.apply(lambda x : get_custcon(list(x)),axis = 1)
            df_com[['combination', 'cust_C']] = df_com['res'].apply(pd.Series)
            
            solution = df_com.loc[df_com["cust_C"].idxmin()][["res"]][0]
            random_com  = tuple(solution[0])
            test_list.append(tuple(solution[0]))
        test_list.append(solution[1])
        test_data.loc[it] = test_list
    return(test_data.loc[test_data["Cust"].idxmin()][test_data.columns[-2]])

start = time.time()
print(list(get_optimisation(2))) # write site number here 
end = time.time()
print("time",end - start)

此代码通常需要 0.038 秒。现在我分享的数据只是样本。我有 250 万行的数据,这需要 75 秒,但我不能为这个过程腾出这么多时间。

我尝试过的代码但没有运气:

def get_custcon(x):
    dfa = df_loop[(df_loop.index.isin(x))]
    dfa.sort_values('Time').groupby('Source').first()
    return  [x,dfa["con"].sum()/dfa["Options"].sum()]


df_com["res"] = df_com.swifter.apply(lambda x : get_custcon(list(x)),axis = 1)

提前致谢。

【问题讨论】:

  • 您有没有机会运行分析器来查找热点?
  • 您的代码调用get_optimised_sites(),但该方法不在您的帖子中。它是get_optimisation() 的别名/错字吗?
  • 是的,这是一个打字错误。谢谢指出
  • 最耗时的函数是get_custcon()。但我必须称它为 site * len(uni_list) Times

标签: python-3.x pandas dataframe optimization pandas-groupby


【解决方案1】:

没有回顾为什么要嵌套 3 个 for 循环,因为您已经说过“慢”部分是 get_custcon(x)

aside:您可能会考虑是否需要嵌套,或者是否有一种方法可以在没有 3 个嵌套循环的情况下获得结果

查看get_custcon(x) 我看到您正在重复对过滤结果进行排序。我认为您可以排序一次并过滤:

df_loop_sorted = df_loop.copy()
df_loop_sorted.sort_values(by = ["Source","Time"], ascending=True).drop_duplicates('Source', keep='first', inplace = True)

def get_custcon(x):
    dfa = df_loop_sorted[(df_loop_sorted["Destination"].isin(x))]
    return  [x,dfa["con"].sum()/dfa["Options"].sum()]

此更改将小数据的执行时间缩短了一半。对于更大的数据,它可能更重要。

请注意,根据您可能需要的数据:

drop_duplicates(['Destination', 'Source'], keep='first', inplace=True)

【讨论】:

  • 非常感谢您的努力。现在在我的主要数据集中只需要 57 秒。我仍在寻找不同的可能性,从逻辑上讲,我必须这样做 .drop_duplicates('Source', keep='first', inplace = True) 再次感谢。
【解决方案2】:

原答案

我想提请您注意,您的 get_custcon 函数的两个版本中的某些代码运行无效。

在您的第二个函数的示例中。您在第一行中实例化 dfa,但随后您的 sort/groupby/first 运行而不被保存。

def get_custcon(x):
    dfa = df_loop[(df_loop.index.isin(x))]
    dfa.sort_values('Time').groupby('Source').first() # <-- this runs for nothing
    return  [x,dfa["con"].sum()/dfa["Options"].sum()]

您可以评论dfa.sort_values… 行并获得完全相同的结果。与提供的数据集相比,这可能会被事实进一步隐藏,对于给定的DestinationSource 值不会重复。

如果您提供更好的玩具数据集(带有边缘案例)会更容易解决您的问题,并简要说明您尝试实现的目标。

编辑以解决您的评论:

在排序值行的 get_custcon 函数中,我使用了 inplace = True。这样我就不需要将排序后的数据框再次保存在变量中。

如果您不是在 dfa 数据帧上执行 inplace 操作,则不是这种情况,而是在副本上执行。

例如看下面的DataFrame:

import pandas as pd
df = pd.DataFrame([[3,2], [1,1], [2, 2], [2, 4], [4, 1]], columns=['A', 'B'])
df

这会打印出以下内容:

    A   B
0   3   2
1   1   1
2   2   2
3   2   4
4   4   1

让我们在适当的位置应用drop_duplicate函数,并检查df的内容:

df.drop_duplicates('A', inplace=True)
df

正如预期的那样:

    A   B
0   3   2
1   1   1
2   2   2
4   4   1

但是,如果您首先应用另一个函数,例如 sort_values,您将获得一个 df 的副本,并且该副本将由 drop_duplicates 就地修改。在原始df 中,这两个操作都没有“保存”。自己看:

df.sort_values(by='A').drop_duplicates('A', inplace=True)
df

输出未修改的df

如果您想保留所有链式更改,您应该执行以下操作:

df = df.sort_values(by='A').drop_duplicates('A')
df

正如预期的那样,这给了我们:

    A   B
1   1   1
2   2   2
0   3   2
4   4   1

【讨论】:

  • 在排序值行的 get_custcon 函数中,我使用了 inplace = True。这样我就不需要将排序后的数据框再次保存在变量中。
  • 在您的第一个 get_custon 函数中,您在 dfa 的副本上运行 inplace=True,因此这对 dfa 本身没有影响。
  • 我更新了我的答案以解决 inplace 评论。您能否详细说明原始代码的目的?这将有助于优化它。
  • 这对我来说在时间上没有任何区别,但感谢您的帮助
  • 嗯,它不应该改进任何东西,我只是指出你的原始代码中有一个错误。现在我相信可以找到对您的代码的改进。但是,我不清楚你想要实现什么。你能解释一下你想用你的计算来实现什么吗?
【解决方案3】:

一些观察:
该代码看起来像是在其中解决的旅行推销员类型的问题。由于理解上下文的范围有限,我不能真正建议,但似乎您可以在 uni_list 上使用并行处理。即以这样一种方式重写代码,我们只检查一个站点开始,并检查所有路径,包括那个等等。如果你能做到这一点,那么你的时间将从 O(site*uni_list) 缩减到 O(site) 左右。
可以进行小的代码优化,取出 setsource = set(df_loop["Source"].unique()) 退出循环。对于小数据集,它不会导致重大变化,但也许您可以检查更大的数据集。
另外,当我更深入地研究 get_custcon(x);它为固定数量的可能性计算 con 和 options 的总和。
一个更好的主意是对所有可能性运行一次这个函数,然后在我们实际调用它时使用一个固定的值字典。
这将减少少量站点的处理时间。更准确地说,您将调用 get_custcon iteration*sites*uni_list 次。现在,在记忆中,您将其称为 (uni_list choose sites) 次。您可以根据实际应用选择更好的工艺。
另外,我会检查数据是如何被读取的。有时,读取 excel 文件需要很长时间;在这种情况下,250 万个数据点最终会花费大量时间。因此,如果是这种情况,您会寻找更好的解决方案,例如 hdf 或羽化文件。

【讨论】:

  • 关于 setsource = set(df_loop["Source"].unique())。在每次新的迭代中,我都需要一个随机点开始。这个迭代次数可以是 1-30 之间的任何值,并且在外部设置源不会每次都给我随机源。我已经为第一个 for 循环实现了多处理,它在范围(迭代)范围内运行,从而节省了大量时间。感谢您的回答。
  • 我不是要你把 random_com = random.sample(setsource, sites) 放在循环之外。您将只保留 setsource 外部,但 random.sample 仍将在内部。这不应该为您提供所需的随机性吗?
  • 好的,明白了。谢谢,这会有所帮助。
猜你喜欢
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 2010-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多