【问题标题】:Avoiding for loop in a pandas data frame when working on selected rows在处理选定行时避免在熊猫数据框中出现 for 循环
【发布时间】:2016-09-24 18:30:19
【问题描述】:

我有一个pandas 数据框df,其中包含每个驱动程序的秒到秒数据(经度、纬度等)。数据框由多个行程组成。有一个名为Event_Type 的功能可用于确定行程的开始和结束:

ignitionOnList = df[df['Event_Type'] == 'Ignition On'].index.tolist()
ignitionOffList = df[df['Event_Type'] == 'Ignition Off'].index.tolist()

所以,假设我在这个数据框中有 5 次行程。 ignitionOnListignitionOffList 的长度为 5。我想专门对每次旅行进行分析并将它们存储在 pandas 数据框中。这是我的工作:

dfTrips = pd.DataFrame({'Date' : [],'Vehicle' : [], 'Trip_Number' : [], 'Start_Time' : [], 'Duration' : [],
                    'Collision': [],'Harsh_Steering' : [], 'Harsh_Deceleration' : [], 'Harsh_Acceleration' : [],
                    'Harsh_Preferred_Speed' : []})
tripCount = -1
tripNumbers = len(ignitionOnList)
for tripNumber in range(tripNumbers):
    tripCount += 1
    dfTemp = df.loc[ignitionOnList[tripNumber]:ignitionOffList[tripNumber]+1]
    # Doing stuff to this temporary data frame and storing them, for example:
    dfTrips.loc[tripCount,'Start_Time'] = dfTemp.loc[0,'Time'].strftime("%H:%M:%S")
    dfTrips.loc[tripCount,'Finish_Time'] = dfTemp.loc[dfTemp.shape[0]-1,'Time'].strftime("%H:%M:%S")
    # Using a function I have defined named `get_steering_risk` to get risky behaviour for each trip
    dfTrips.loc[tripCount,'Harsh_Deceleration'] = get_deceleration_risk(dfTemp)
    dfTrips.loc[tripCount,'Harsh_Steering'] = get_steering_risk(dfTemp)

这行得通。但我猜在没有 for 循环的情况下,在 Python 中有更好的方法可以做到这一点。我不确定我是否可以简单地使用apply,因为我没有将相同的函数应用于整个数据框。

另一种方法可能是重新定义函数,以便它们在df 中生成一列 并将它们应用于整个数据框,然后汇总每次行程的结果。例如,get_steering_risk 函数可以定义为在df 中每秒生成01,然后每次行程的1s 的百分比将是Harsh_SteeringdfTrips 中。但是,某些功能不能应用于整个数据框。例如,一个函数对速度与加速度进行回归,它应该逐个行程地完成。解决这个问题的最佳方法是什么?谢谢。

【问题讨论】:

  • 你的秒到秒数据框很大吗?我经常发现打开一次大 df 然后对小的临时 df 进行所有分析会更节省内存,就像在您的示例中一样。
  • 另外,循环的读取端看起来效率并不低。进入 dfTemp 的每个行块只读取一次。
  • @andrew df 数据框一点也不大。它来自一个 2 Mb 的 csv 文件。但是,我有数千个这样的文件需要输入到我的代码中,这就是为什么我尽可能让代码更有效率。
  • 根据我在下面的回答,我认为您会看到由于逐行构建dfTrips 而对性能造成的影响要大得多,然后您将从循环中看到。 Pandas read_csv 解析器也非常快。我不会担心 I/O 开销。

标签: python loops pandas apply


【解决方案1】:

我怀疑任何性能问题实际上可能是由于您的成长方式dfTrips。我发现创建许多小的,甚至是单行(或单列)数据帧,然后使用pd.concat 将它们全部连接起来,然后尝试逐行增长一个df,速度要快几个数量级。

我问了一个类似的问题。在接受的答案中查看concat 的速度有多快。

Creating large Pandas DataFrames: preallocation vs append vs concat

[编辑] 下面说明了为什么每次迭代都覆盖 temp df 并将其附加到列表中不起作用(请参阅下面的 cmets):

df = pd.DataFrame(columns=np.arange(5))
df_list=  []
for i in np.arange(5):
    df.loc[0,:] = i
    df_list.append(df)
for d in df_list:
    print d
    print

【讨论】:

  • 这是有道理的。我会试一试。因此,在 for 循环的每个步骤中,我将定义 dfTripsStep = pd.DataFrame({}),然后使用类似 dfTripsStep.loc[0,'Start_Time'] = '3:00' 的内容在其中创建一行,然后在每个步骤的 for 循环结束时,我将执行 dfTrips.append(dfTripsStep),最后在外面for 循环我会做pd.concat(fTrips)。在每个步骤中以这种方式制作这些 1 行 dfTripsStep 听起来是否合理,或者可能有更好的方法?
  • 使用上面的方法,通过13个文件的时间减少了2.9%,这很好,但不是很明显。我假设如果我有更多文件并且dfTrips 有更多行,那么差异会更显着。
  • 您的版本中有dfTripsa 列表吗?它不应该是 df(有一个 df.append 方法,它很昂贵)。
  • 另外,我还会逐列增长dfTripsStep。我会写一个助手来一次初始化它: temp_dat = generate_measures(dfTemp) # 产生你所有的度量,长度与 cols 相同
  • dfTripsStep = pd.DataFrame(data=temp_dat, columns=cols)
【解决方案2】:

我不确定这是否会节省时间,但您可以(在某种程度上)使用groupby 避免循环。首先,您将定义一个新列,例如trip_number,来索引每个唯一的行程(这可能仍涉及遍历行程编号)。然后按trip_number分组。

您可以使用apply 将单个函数单独应用于每个组。

最后,您将使用水平 concat 将它们加入到您的输出 df 中。

请参阅文档的“Felixble apply”部分。

grouped = df.groupby('trip_num')
decel_df = grouped.apply(get_deceleration_risk)
steer_df = grouped.apply(get_steering_risk)
...
dfTrips = pd.concat([decel_df, steer_df, ...], axis=1) 
dfTrips.columns = ['Harsh_Deceleration', 'Harsh_Steering', ...]

【讨论】:

  • 如果您尝试此方法,请报告速度。我很好奇它是否有显着不同。
  • 我试过这个方法,效果很好。它比 for 循环稍快。问题是我有 13 个文件,每个文件中有 5-6 次行程。我想如果我在每个文件中有更多的行程,这种方法会明显更快。无论如何,这肯定是一种更清洁的方法。谢谢。
  • 有趣。我将不得不对较大的 dfs 进行一些时间测试。感谢您的反馈
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-25
  • 1970-01-01
  • 2022-01-08
  • 2021-06-08
  • 2015-09-19
  • 1970-01-01
  • 2015-10-18
相关资源
最近更新 更多