[FIXED] Berechnung der Rollgeschwindigkeit in einem PandasDataframe

Ausgabe

Ich habe folgende Herausforderung: Ich habe einen PandasDataframe mit Informationen über eine eindeutige ArucoID, eine eindeutige FrameID und zugehörige Koordinaten in einem Koordinatensystem. Zum Beispiel so:

# 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)

Jetzt berechne ich den Abstand zwischen den xPos als gleitenden Mittelwert für den nach ArucoID gruppierten 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)

Und jetzt komme ich in Schwierigkeiten: Ich möchte die Geschwindigkeit [s] berechnen. Das wäre v = dX / (time[-1] – time[0] / framerate), wobei time[-1] die letzte Frame-ID des rollenden Fensters ist, t[0] die aktuelle Frame-ID und die Framerate 30 Frames/pro/ zweite.

Ich begann mit (rolling_window=3, min_periods=1):

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

das ist die Berechnung der Rollstrecke. Was ich eigentlich gerne machen würde wäre sowas:

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

#print(df)

Alle mögliche Vorschläge würden geschätzt. Vielen Dank im Voraus!

Lösung

Ich gehe davon aus, dass Sie für jedes Gerät und jeden Versuch spezifische mechanische Geschwindigkeiten berechnen möchten.

Datensatz vorbereiten

Beginnen wir mit Ihren Rohdaten:

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) 
])

Der Schlüssel ist, Ihre Positionsaufzeichnungen zu verschieben, um die Entfernung berechnen zu können.

Dazu sortieren wir die Aufzeichnungen für diese Operation in natürlicher Reihenfolge und verzögern sie dann nach Gerät und Versuch:

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)

Jetzt sind Ihre Daten richtig ausgerichtet:

#     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
# ...

Geschwindigkeitsberechnungen

Distanz

Dann können wir die euklidische Distanz leicht berechnen:

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)

Punktschätzungen für Geschwindigkeit

Und gleichzeitig Punktschätzung und gleitende Durchschnittsgeschwindigkeiten:

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 

Durchschnittsgeschwindigkeit

Danach können wir nach Gerät und Versuch aggregieren, um die Gesamtstrecke und die Anzahl der Frames zu erhalten:

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

Wir können auch die durchschnittliche mechanische Geschwindigkeit jedes Geräts und Versuchs berechnen:

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

Und führen Sie alle Informationen zusammen:

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"]

Um Durchschnittsgeschwindigkeiten mit allen Aufzeichnungen in Einklang zu bringen.

Nachbearbeitung

Schließlich können wir diese Datensätze drehen, um sie einfach zu navigieren und zu rendern:

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

Für den von Ihnen bereitgestellten spezifischen Datensatz haben wir die folgenden gleitenden Durchschnitte:

Geben Sie hier die Bildbeschreibung ein

Und wie es mit der Durchschnittsgeschwindigkeit verglichen wird:

Geben Sie hier die Bildbeschreibung ein


Beantwortet von –
jlandercy


Antwort geprüft von –
Marilyn (FixError Volunteer)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like