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:
Und wie es mit der Durchschnittsgeschwindigkeit verglichen wird:
Beantwortet von – jlandercy
Antwort geprüft von – Marilyn (FixError Volunteer)