【问题标题】:Calculation of rolling speed in a PandasDataframePandas Dataframe 中滚动速度的计算
【发布时间】:2022-10-05 11:54:58
【问题描述】:

我有以下挑战:我有一个 PandasDataframe,其中包含有关唯一 ArucoID、唯一 frameID 和坐标系中相关坐标的信息。例如像这样:

# import pandas library
import pandas as pd
# lst_of_dfs = []
# dictionary with list object of values
data1 = {
     \'frameID\' : [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
     \'xPos\' : [10.0, 10.5, 11.0, 12.0, 13, 4.0, 5.0, 6.0, 7.0, 9.0, 1.5, 2.0, 2.5, 3.0, 4.0 ],
     \'yPos\' : [-0.2, -0.1, -0.1, 0.0, 0.0, 0.2, 0.2, -0.1, 0.0, 0.05, -0.2, -0.1, 0.0, 0.1, 0.05],
     \'ArucoID\' : [910, 910, 910, 910, 910, 898, 898, 898, 898, 898, 912, 912, 912, 912, 912],
     \'Subtrial\' : [\'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\', \'01\']
     }
df1 = pd.DataFrame(data1)

   
data2 = {
     \'frameID\' : [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
     \'xPos\' : [9.4, 9.5, 9.0, 9.0, 10, 3.0, 4.0, 5.0, 6.0, 7.0, 2.5, 3.0, 3.5, 3.5, 5.0 ],
     \'yPos\' : [-0.2, -0.1, -0.1, 0.0, 0.0, 0.2, 0.2, -0.1, 0.0, 0.05, -0.2, -0.1, 0.0, 0.1, 0.05],
     \'ArucoID\' : [910, 910, 910, 910, 910, 898, 898, 898, 898, 898, 912, 912, 912, 912, 912],
     \'Subtrial\' : [\'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\', \'02\']
     }
df2 = pd.DataFrame(data2)

 
lst_of_dfs = [df1,df2]
 
# creating a Dataframe object 
df_TrajData = pd.concat(lst_of_dfs)

#print(df_TrajData)

现在我计算 xPos 之间的距离作为按 ArucoID 分组的 DataFrame 的滚动平均值:

#calculation of current distance of each ArucoID as rolling mean over a window of n frames (n is set as 2 frames for testing)

all_data = []    
df_grouped = df_TrajData.groupby(\'ArucoID\')
for key, data in df_grouped:
    #calc distance covered in window     
    dX = data[\'xPos\'] - data[\'xPos\'].shift(2)
    #print(dX)
       
    data[\'dX\'] = dX
    
    all_data.append(data)
    
df = pd.concat(all_data)
#print(df)

现在我遇到了麻烦:我想计算速度 [s]。这将是 v = dX / (time[-1] - time[0] / framerate),其中 time[-1] 是滚动窗口的最后一个 frameID,t[0] 当前 frameID 和 framerate 是 30 帧/每/第二。

我从 (rolling_window=3, min_periods=1) 开始:

df[\'speed\'] = df.groupby(\'ArucoID\')[\'dX\'].transform(lambda x: x.rolling(3, 1).mean())

这是滚动距离的计算。我实际上想做的是这样的:

df[\'speed\'] = df.groupby(\'ArucoID\')[\'dX\'].transform(lambda s: s.rolling(3, min_periods=1).mean() / (t[-1] - t[0] /framerate))

#print(df)

任何建议,将不胜感激。提前谢谢了!

更新的问题:

我会带着一个理解问题回来,以便进一步处理。 (我不确定我是否应该提出一个新问题,或者这是否不适合在主题上更好地直接解决已解决的问题)。

我想计算 ArucoID i 和前一个 ArucoID i+1 在每个 frameID 的移动方向上的距离(前进)。然后我也想将滚动平均值应用于这个距离(rolling_headway)。

@jlandercy 提出的解决方案应该类似地工作:

1 按 frameID 对 df 进行排序。这里重要的是 ArucoID 保持未排序,因为顺序不一定是升序。换句话说,我想保持 ArucoID 的顺序。

df = df_TrajData.sort_values([\"Subtrial\", \"frameID\"])
  1. 确定移动的 x 位置(移动方向在 x 方向):
shifted = df.groupby([\"Subtrial\"]).shift(-1)
#print(shifted)
shifted = shifted.drop(\"frameID\", axis=1).rename(columns=lambda x: x + \"_\")
data = pd.concat([df, shifted], axis=1)
  1. 在点(帧)的车距计算:
def dX(x):
    return np.sqrt(np.power(x[\"xPos\"] - x[\"xPos_\"], 2))
        
data[\'point_headway\'] = data.apply(dX, axis=1)
  1. 按 ArucoID 和 Subtrial 对 dfs 进行排序(因为我希望获得特定 ArucoID 的滚动平均值),然后应用滚动平均值(假设此处的窗口大小为 3)。
data[\"rolling_headway\"] = data.sort_values([\"Subtrial\", \"ArucoID\",\'frameID\']).groupby([\"Subtrial\", \"ArucoID\"]).rolling(3, min_periods=2).mean()[\"point_headway\"].values
#print(data)

结果是:

    frameID  xPos  yPos  ...  ArucoID_ point_headway  rolling_headway
5         1   4.0  0.20  ...     912.0           2.5         2.750000
6         2   5.0  0.20  ...     912.0           3.0         4.166667
7         3   6.0 -0.10  ...     912.0           3.5         5.500000
8         4   7.0  0.00  ...     912.0           4.0              NaN
9         5   9.0  0.05  ...     912.0           5.0         9.500000

我的预期(见最后一栏)

    frameID  xPos  yPos  ...  ArucoID_ point_headway    expected
5         1   4.0  0.20  ...     912.0           2.5         NaN
6         2   5.0  0.20  ...     912.0           3.0         NaN
7         3   6.0 -0.10  ...     912.0           3.5         3.000000 (2.5+3.0+3.5)/3
8         4   7.0  0.00  ...     912.0           4.0         3.500000 (3.0+3.5+4.0)/3
9         5   9.0  0.05  ...     912.0           5.0         4.166667 (3.5+4.0+5.0)/3

我的思维错误在哪里?我想我排序错了,不是吗?

提前感谢您的建议!

    标签: python pandas


    【解决方案1】:

    我假设您要计算每个设备和试验的特定机械速度。

    准备数据集

    让我们从您的原始数据开始:

    import numpy as np
    import pandas as pd
    
    data1 = {
        'ArucoID' : [910, 910, 910, 910, 910, 898, 898, 898, 898, 898, 912, 912, 912, 912, 912],
        'Subtrial' : ['01', '01', '01', '01', '01', '01', '01', '01', '01', '01', '01', '01', '01', '01', '01'],
        'frameID' : [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
        'xPos' : [10.0, 10.5, 11.0, 12.0, 13, 4.0, 5.0, 6.0, 7.0, 9.0, 1.5, 2.0, 2.5, 3.0, 4.0 ],
        'yPos' : [-0.2, -0.1, -0.1, 0.0, 0.0, 0.2, 0.2, -0.1, 0.0, 0.05, -0.2, -0.1, 0.0, 0.1, 0.05],
    }
    
    data2 = {
        'ArucoID' : [910, 910, 910, 910, 910, 898, 898, 898, 898, 898, 912, 912, 912, 912, 912],
        'Subtrial' : ['02', '02', '02', '02', '02', '02', '02', '02', '02', '02', '02', '02', '02', '02', '02'],
        'frameID' : [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
        'xPos' : [9.4, 9.5, 9.0, 9.0, 10, 3.0, 4.0, 5.0, 6.0, 7.0, 2.5, 3.0, 3.5, 3.5, 5.0 ],
        'yPos' : [-0.2, -0.1, -0.1, 0.0, 0.0, 0.2, 0.2, -0.1, 0.0, 0.05, -0.2, -0.1, 0.0, 0.1, 0.05],
    }
    
    df = pd.concat([
        pd.DataFrame(data1),
        pd.DataFrame(data2) 
    ])
    

    关键是改变你的位置记录,以便能够计算距离。

    为此,我们为此操作按自然顺序对记录进行排序,然后按设备和试验对它们进行滞后:

    df = df.sort_values(["ArucoID", "Subtrial", "frameID"])
    shifted = df.groupby(["ArucoID", "Subtrial"]).shift(-1)
    shifted = shifted.drop("frameID", axis=1).rename(columns=lambda x: x + "_")
    data = pd.concat([df, shifted], axis=1)
    

    现在您的数据已正确对齐:

    #     ArucoID Subtrial  frameID  xPos  yPos  xPos_  yPos_
    # 5       898       01        1   4.0  0.20    5.0   0.20
    # 6       898       01        2   5.0  0.20    6.0  -0.10
    # 7       898       01        3   6.0 -0.10    7.0   0.00
    # 8       898       01        4   7.0  0.00    9.0   0.05
    # 9       898       01        5   9.0  0.05    NaN    NaN
    # 5       898       02        1   3.0  0.20    4.0   0.20
    # ...
    

    速度计算

    距离

    然后我们可以很容易地计算欧几里得距离:

    def distance(x):
        return np.sqrt(np.power(x["xPos"] - x["xPos_"], 2) + np.power(x["yPos"] - x["yPos_"], 2))
    
    data["dist"] = data.apply(distance, axis=1)
    

    速度点估计

    同时估计和移动平均速度:

    data["point_speed"] = data["dist"]/(1/30)
    data["mov_speed"] = data.groupby(["ArucoID", "Subtrial"]).rolling(3, min_periods=1).mean()["point_speed"].values
    
        # ArucoID Subtrial  frameID  xPos  yPos  xPos_  yPos_      dist point_speed  mov_speed  
    # 5       898       01        1   4.0  0.20    5.0   0.20  1.000000   30.000000  30.000000  
    # 6       898       01        2   5.0  0.20    6.0  -0.10  1.044031   31.320920  30.660460  
    # 7       898       01        3   6.0 -0.10    7.0   0.00  1.004988   30.149627  30.490182  
    # 8       898       01        4   7.0  0.00    9.0   0.05  2.000625   60.018747  40.496431  
    # 9       898       01        5   9.0  0.05    NaN    NaN       NaN         NaN  45.084187  
    # 5       898       02        1   3.0  0.20    4.0   0.20  1.000000   30.000000  30.000000 
    

    平均速度

    之后我们可以通过设备和试验进行聚合,得到总距离和帧数:

    final = data.groupby(["ArucoID", "Subtrial"]).agg({"dist": "sum", "frameID": "count"}).rename(columns={"frameID": "count"})
    
    #                       dist  count
    # ArucoID Subtrial                 
    # 898     01        5.049643      5
    #         02        4.050267      5
    # 910     01        3.014890      5
    #         02        1.741421      5
    # 912     01        2.530955      5
    #         02        2.620637      5
    

    我们还可以计算每个设备和试验的平均机械速度:

    def speed(x, frame_time=1.):
        return x["dist"]/((x["count"] - 1)*frame_time)
    
    final["speed"] = final.apply(speed, axis=1, frame_time=1/30)
    
    #                       dist  count      speed
    # ArucoID Subtrial                            
    # 898     01        5.049643      5  37.872323
    #         02        4.050267      5  30.377006
    # 910     01        3.014890      5  22.611671
    #         02        1.741421      5  13.060660
    # 912     01        2.530955      5  18.982163
    #         02        2.620637      5  19.654778
    

    并将所有信息合并在一起:

    final = data.merge(final["avg_speed"], left_on=["ArucoID", "Subtrial"], right_index=True)
    final["speed_ratio"] = final["mov_speed"]/final["avg_speed"]
    final["speed_excess"] = 1. - final["speed_ratio"]
    

    使平均速度与所有记录保持一致。

    后期处理

    最后,我们可以旋转这些记录以轻松导航和呈现它们:

    cross = final.pivot_table(index="frameID", columns=["ArucoID", "Subtrial"], values=["point_speed", "mov_speed", "avg_speed", "speed_ratio", "speed_excess"])
    

    对于您提供的特定数据集,我们有以下移动平均线:

    以及它与平均速度的比较:

    【讨论】:

    • 非常非常感谢你!这是一个绝妙的答案。诀窍是——正如你所猜到的——按 ArucoID、Subtrial(感谢您预期我稍后会想通过 Subtrials 区分)和 frameID 进行排序。感谢您的帮助和对此的详细解释!
    猜你喜欢
    • 2017-06-27
    • 2019-01-03
    • 1970-01-01
    • 2013-02-24
    • 2021-02-26
    • 2022-01-07
    • 1970-01-01
    • 2017-12-15
    • 2021-08-05
    相关资源
    最近更新 更多