在处理选定行时避免在 pandas 数据框中进行循环

Avoiding for loop in a pandas data frame when working on selected rows

我有一个 pandas 数据框 df,每个 driver 都有 second-to-second 数据(经度、纬度等)。数据框由多个行程组成。有一个叫做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)

这行得通。但我猜测在 Python 中有更好的方法可以在没有 for 循环的情况下执行此操作。我不确定我是否可以简单地使用 apply 因为我没有对整个数据框应用相同的函数。

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

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

我问过类似的问题。查看接受的答案 concat 有多快。

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

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

我不确定它是否会节省时间,但您可以(某种程度上)通过使用 groupby 来避免循环。首先,您将定义一个新列,比如 trip_number,为每个唯一的旅行编制索引(这可能仍然涉及循环 tripNumbers)。然后按 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', ...]