Python 处理选定行时避免数据帧中的for循环
我有一个Python 处理选定行时避免数据帧中的for循环,python,loops,pandas,apply,Python,Loops,Pandas,Apply,我有一个pandas数据框,df,它为每个驾驶员提供每秒的数据(经度、纬度等)。数据帧由多个行程组成。有一种称为事件类型的功能,可用于确定行程的开始和结束: ignitionOnList = df[df['Event_Type'] == 'Ignition On'].index.tolist() ignitionOffList = df[df['Event_Type'] == 'Ignition Off'].index.tolist() 假设我在这个数据框中有5次旅行。IgnitionList
pandas
数据框,df
,它为每个驾驶员提供每秒的数据(经度、纬度等)。数据帧由多个行程组成。有一种称为事件类型的功能,可用于确定行程的开始和结束:
ignitionOnList = df[df['Event_Type'] == 'Ignition On'].index.tolist()
ignitionOffList = df[df['Event_Type'] == 'Ignition Off'].index.tolist()
假设我在这个数据框中有5次旅行。IgnitionList
和ignitionOffList
的长度为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)
这很有效。但我猜在Python中有更好的方法来实现这一点,而不需要for循环。我不确定我是否可以简单地使用apply
,因为我没有对整个数据帧应用相同的函数
另一种方法是重新定义函数,以便它们在df
并将其应用于整个数据帧,然后汇总每次旅行的结果。例如,get\u steering\u risk
功能可以定义为在df
中每秒使0
或1
,然后在dfTrips
中,每次行程1
s的百分比将是Harsh\u steering
。但是,有些函数不能应用于整个数据帧。例如,一个函数将速度与加速度进行回归,并且应该一次一次地进行。最好的方法是什么?谢谢。我怀疑任何性能问题实际上都可能是由于您不断增长的dfTrips
造成的。我发现,创建许多小的、甚至是单行(或单列)数据帧,然后使用pd.concat
将它们全部连接起来,然后尝试一行一行地增长一个df,速度要快几个数量级
我问了一个类似的问题。查看公认答案中的concat
快了多少
[编辑]
下面是一个例子,说明了为什么每次迭代都覆盖临时df并将其附加到列表中是行不通的(请参阅下面的注释):
我不确定这是否会节省时间,但您可以(某种程度上)通过使用groupby
来避免循环。首先,您需要定义一个新的列,例如trip\u number
,以索引每个唯一的trip(这可能仍然需要在tripNumber上循环)。然后按行程编号分组
您可以使用apply
将单个函数分别应用于每个组
最后,您将使用水平concat
将它们连接到输出df中
请参阅文档的“”部分
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', ...]
您的二到二数据帧是否非常大?我经常发现,打开一次大的df,然后对小的临时df进行所有分析,就像在您的示例中一样,内存效率更高。此外,循环的读取端看起来效率也不是很低。进入dfTemp的每个行块只读取一次。@andrewdf
数据帧一点也不大。它来自一个2MB的csv文件。然而,我有数千个这样的文件需要输入到我的代码中,这就是为什么我要尽可能提高代码的效率。根据我下面的回答,我认为你会看到性能受到更坏的影响,因为一行一行地构建dfTrips
,然后你会从循环中看到。Pandasread_csv
解析器的速度也非常快。我不会担心I/O开销,这是有道理的。我要试一试。因此,在for循环的每一步中,我将定义dfTripsStep=pd.DataFrame({})
,然后使用类似于dfTripsStep.loc[0,'Start\u Time']='3:00'
的内容在for循环的每一步末尾,我将执行dfTrips.append(dfTripsStep)
最后,在for循环之外,我将执行pd.concat(fTrips)
。在每个步骤中以这种方式制作这些1行dfTripsStep
对您来说合理吗?或者可能有更好的方法来实现这一点?使用上述方法,传递13个文件的时间减少了2.9%
,这很好,但没有太大意义。我假设如果我有更多的文件,并且dfTrips
有更多的行,那么差异将更加显著。dfTrips
是您版本中的列表吗?它不应该是一个df(有一个df.append方法,这很昂贵)。此外,我还将逐列增加dfTripsStep
。我将编写一个助手来一次性初始化它:temp_dat=generate_measures(dfTemp)#生成所有度量值,长度与colsdfTripsStep=pd.DataFrame(data=temp_dat,columns=cols)如果您尝试此方法,请报告速度。我很好奇它是否有明显的不同。我尝试过这种方法,它是有效的。它比for循环稍微快一点。问题是我有13个文件,每个文件有5-6次行程。我想如果我在每个文件中有更多的行程,这个方法会明显更快。无论如何,这肯定是一种更干净的方法。谢谢,很有趣。我将不得不在较大的dfs上进行一些时间测试。谢谢你的回复
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', ...]