Cen*_*ius 7 python numpy dataframe pandas
从广义上讲,我试图计算许多不同试验的红色路径/轨迹有多少落在黑色路径之间(见下图)。我圈出了几个例子,对于 (0, 1, 3) 大约 30-40% 的红色路径落在两条黑色路径之间,但对于 (2, 1, 3) 只有大约 1-2%红色路径位于两条黑色路径之间。
我有两个数据帧,df_R 和 df_H。
df_R 包含红色路径的位置数据(在 X 和 Z 中)。请注意,X 和 Z 都是位置/空间数据。这些数据没有类似日期/时间的索引。另请注意,这是来自 Unity 应用程序的 2D 数据(出于某种原因,X 和 Z 相当于 Y 和 X)。两个数据帧中的所有路径/轨迹的长度均为 1000 点。
df_R 预览:
(0, 1, 1)_mean_X (0, 1, 1)_mean_Z ... (2, 2, 3)_mean_X (2, 2, 3)_mean_Z
0 -15.856713 5.002617 ... -15.600160 -5.010470
1 -15.831320 5.003529 ... -15.566172 -5.012251
2 -15.805927 5.004441 ... -15.532184 -5.014032
3 -15.780534 5.005353 ... -15.498196 -5.015814
4 -15.755141 5.006265 ... -15.464208 -5.017595
.. ... ... ... ... ...
95 -12.818362 5.429729 ... -12.391177 -5.391595
96 -12.783905 5.437335 ... -12.357563 -5.396919
97 -12.749456 5.444990 ... -12.323950 -5.402243
98 -12.715017 5.452697 ... -12.290336 -5.407567
99 -12.680594 5.460469 ... -12.256722 -5.412891
Run Code Online (Sandbox Code Playgroud)
df_H 包含黑色路径的位置数据,其中包括 X 和 Z 的“顶部”和“底部”列,对应于每个图中的两条黑色路径。
df_H 预览:
(0, 1, 1)_top_X (0, 1, 1)_bottom_X ... (2, 2, 3)_top_Z (2, 2, 3)_bottom_Z
0 -16.000000 -16.000000 ... -5.000000 -5.000000
1 -16.000000 -16.000000 ... -5.000000 -5.000000
2 -16.000000 -16.000000 ... -5.000000 -5.000000
3 -16.000000 -16.000000 ... -5.000000 -5.000000
4 -16.000000 -16.000000 ... -5.000000 -5.000000
.. ... ... ... ... ...
95 -15.000971 -15.417215 ... -4.993461 -5.011372
96 -14.979947 -15.402014 ... -4.993399 -5.013007
97 -14.957949 -15.385840 ... -4.993291 -5.014463
98 -14.934171 -15.368649 ... -4.993186 -5.015692
99 -14.908484 -15.349371 ... -4.993069 -5.016940
Run Code Online (Sandbox Code Playgroud)
我需要查看 df_R 中的 X/Z 值是否在 df_H 中的 X/Z 值范围内。但是,解决方案不需要依赖时间或索引(或逐行比较)。
我一直试图实现这一点,但被卡住了。这是我一直在尝试的,但它不起作用,因为它依赖于时间。
import pandas as pd
import numpy as np
def CI_analysis(df_H, df_R):
# separate X & Z
df_H_top_X = df_H.filter(regex='top_X')
df_H_bottom_X = df_H.filter(regex='bottom_X')
df_H_top_Z = df_H.filter(regex='top_Z')
df_H_bottom_Z = df_H.filter(regex='bottom_Z')
df_R_X = CI_raycast.filter(regex='mean_X')
df_R_Z = CI_raycast.filter(regex='mean_Z')
# check if X is within the range of top & bottom X
CI_inside_X = pd.DataFrame()
for col in df_R_X:
temp = []
c = 0
for i, val in df_R_X[col].iteritems():
if (val < df_H_top_X.iloc[i,c]) & (val > df_H_bottom_X.iloc[i,c]):
temp.append(1)
else:
temp.append(0)
CI_inside_X[col] = temp
c = c+1
# check if Z is within the range of top & bottom Z
CI_inside_Z = pd.DataFrame()
for col in df_R_Z:
temp = []
# print(col)
c = 0
for i, val in df_R_Z[col].iteritems():
if (val < df_H_top_Z.iloc[i,c]) & (val > df_H_bottom_Z.iloc[i,c]):
temp.append(1)
else:
temp.append(0)
CI_inside_Z[col] = temp
c = c+1
# Check if X & Z were both in-between the top & bottom trajectories
CI_inside = pd.DataFrame()
for col in CI_inside_X:
temp = []
c = 0
for i,row in CI_inside_X[col].iteritems():
if (row == 1) & (CI_inside_Z.iloc[i,c] == 1):
temp.append(1)
else:
temp.append(0)
CI_inside[col] = temp
c = c+1
CI_inside_avg = pd.DataFrame(CI_inside.mean(axis=0)).transpose()
return CI_inside_X, CI_inside_Z, CI_inside, CI_inside_avg
Run Code Online (Sandbox Code Playgroud)
最后,这里是重现两个数据帧 df_R 和 df_H(带有随机数)的代码:
df_R_cols = ['(0, 1, 1)_mean_X', '(0, 1, 1)_mean_Z', '(0, 1, 2)_mean_X',
'(0, 1, 2)_mean_Z', '(0, 1, 3)_mean_X', '(0, 1, 3)_mean_Z',
'(0, 2, 1)_mean_X', '(0, 2, 1)_mean_Z', '(0, 2, 2)_mean_X',
'(0, 2, 2)_mean_Z', '(0, 2, 3)_mean_X', '(0, 2, 3)_mean_Z',
'(1, 1, 1)_mean_X', '(1, 1, 1)_mean_Z', '(1, 1, 2)_mean_X',
'(1, 1, 2)_mean_Z', '(1, 1, 3)_mean_X', '(1, 1, 3)_mean_Z',
'(1, 2, 1)_mean_X', '(1, 2, 1)_mean_Z', '(1, 2, 2)_mean_X',
'(1, 2, 2)_mean_Z', '(1, 2, 3)_mean_X', '(1, 2, 3)_mean_Z',
'(2, 1, 1)_mean_X', '(2, 1, 1)_mean_Z', '(2, 1, 2)_mean_X',
'(2, 1, 2)_mean_Z', '(2, 1, 3)_mean_X', '(2, 1, 3)_mean_Z',
'(2, 2, 1)_mean_X', '(2, 2, 1)_mean_Z', '(2, 2, 2)_mean_X',
'(2, 2, 2)_mean_Z', '(2, 2, 3)_mean_X', '(2, 2, 3)_mean_Z']
df_H_cols = ['(0, 1, 1)_top_X', '(0, 1, 1)_bottom_X', '(0, 1, 1)_top_Z',
'(0, 1, 1)_bottom_Z', '(0, 1, 2)_top_X', '(0, 1, 2)_bottom_X',
'(0, 1, 2)_top_Z', '(0, 1, 2)_bottom_Z', '(0, 1, 3)_top_X',
'(0, 1, 3)_bottom_X', '(0, 1, 3)_top_Z', '(0, 1, 3)_bottom_Z',
'(0, 2, 1)_top_X', '(0, 2, 1)_bottom_X', '(0, 2, 1)_top_Z',
'(0, 2, 1)_bottom_Z', '(0, 2, 2)_top_X', '(0, 2, 2)_bottom_X',
'(0, 2, 2)_top_Z', '(0, 2, 2)_bottom_Z', '(0, 2, 3)_top_X',
'(0, 2, 3)_bottom_X', '(0, 2, 3)_top_Z', '(0, 2, 3)_bottom_Z',
'(1, 1, 1)_top_X', '(1, 1, 1)_bottom_X', '(1, 1, 1)_top_Z',
'(1, 1, 1)_bottom_Z', '(1, 1, 2)_top_X', '(1, 1, 2)_bottom_X',
'(1, 1, 2)_top_Z', '(1, 1, 2)_bottom_Z', '(1, 1, 3)_top_X',
'(1, 1, 3)_bottom_X', '(1, 1, 3)_top_Z', '(1, 1, 3)_bottom_Z',
'(1, 2, 1)_top_X', '(1, 2, 1)_bottom_X', '(1, 2, 1)_top_Z',
'(1, 2, 1)_bottom_Z', '(1, 2, 2)_top_X', '(1, 2, 2)_bottom_X',
'(1, 2, 2)_top_Z', '(1, 2, 2)_bottom_Z', '(1, 2, 3)_top_X',
'(1, 2, 3)_bottom_X', '(1, 2, 3)_top_Z', '(1, 2, 3)_bottom_Z',
'(2, 1, 1)_top_X', '(2, 1, 1)_bottom_X', '(2, 1, 1)_top_Z',
'(2, 1, 1)_bottom_Z', '(2, 1, 2)_top_X', '(2, 1, 2)_bottom_X',
'(2, 1, 2)_top_Z', '(2, 1, 2)_bottom_Z', '(2, 1, 3)_top_X',
'(2, 1, 3)_bottom_X', '(2, 1, 3)_top_Z', '(2, 1, 3)_bottom_Z',
'(2, 2, 1)_top_X', '(2, 2, 1)_bottom_X', '(2, 2, 1)_top_Z',
'(2, 2, 1)_bottom_Z', '(2, 2, 2)_top_X', '(2, 2, 2)_bottom_X',
'(2, 2, 2)_top_Z', '(2, 2, 2)_bottom_Z', '(2, 2, 3)_top_X',
'(2, 2, 3)_bottom_X', '(2, 2, 3)_top_Z', '(2, 2, 3)_bottom_Z']
df_R = pd.DataFrame(np.random.randint(0,100,size=(1000, 36)), columns=df_R_cols)
df_H = pd.DataFrame(np.random.randint(0,100,size=(1000, 72)), columns=df_H_cols)
Run Code Online (Sandbox Code Playgroud)
只是一个想法
如果我理解正确的话,问题在于数据是在不同点采样的。所以你不能只比较每一行的值。有时底线与顶线互换。
我的想法是现在以与红色轨迹相同的 x 值插入黑色轨迹。我的回答集中在这个想法上。我从之前的答案中借用了一些代码来迭代数据集。
df_H = pd.read_pickle('df_H.pickle')
df_R = pd.read_pickle('df_R.pickle')
dfh_groups = [df_H.columns[x:x + 4] for x in range(0, len(df_H.columns), 4)]
dfr_groups = [df_R.columns[x:x + 2] for x in range(0, len(df_R.columns), 2)]
df_result = pd.DataFrame(columns=['Percentage'])
for i in range(len(dfr_groups)):
label = dfr_groups[i][0].split('_')[0]
X_R = df_R[dfr_groups[i][0]].to_numpy()
Y_R = df_R[dfr_groups[i][1]].to_numpy()
X_H_Top = df_H[dfh_groups[i][0]].to_numpy()
Y_H_Top = df_H[dfh_groups[i][1]].to_numpy()
X_H_Bottom = df_H[dfh_groups[i][2]].to_numpy()
Y_H_Bottom = df_H[dfh_groups[i][3]].to_numpy()
# Interpolate df_H to match the data points from df_R
bottom = interpolate.interp1d(X_H_Bottom,Y_H_Bottom)
top = interpolate.interp1d(X_H_Top,Y_H_Top)
# Respect the interpolation boundaries, so drop every row not in range from X_H_(Bottom/Top)
X_H_Bottom = X_R[(X_R > np.amin(X_H_Bottom)) & (X_R < np.amax(X_H_Bottom))]
X_H_Top = X_R[(X_R > np.amin(X_H_Top)) & (X_R < np.amax(X_H_Top))]
minimal_X = np.intersect1d(X_H_Bottom, X_H_Top)
# Calculate the new values an the data points from df_R
Y_I_Bottom = bottom(minimal_X)
Y_I_Top = top(minimal_X)
#Plot
'''
plt.plot(X_R, Y_R,'r-',minimal_X, Y_I_Bottom,'k-', minimal_X, Y_I_Top,'k-')
plt.show()
'''
# Count datapoints of df_R within bottom and top
minimal_x_idx = 0
nr_points_within = 0
for i in range(0,len(X_R)):
if minimal_x_idx >= len(minimal_X):
break
elif X_R[i] != minimal_X[minimal_x_idx]:
continue
else:
# Check if datapoint within even if bottom and top changed
if (Y_R[i] > np.amin(Y_I_Bottom[minimal_x_idx]) and Y_R[i] < np.amax(Y_I_Top[minimal_x_idx]))\
or (Y_R[i] < np.amin(Y_I_Bottom[minimal_x_idx]) and Y_R[i] > np.amax(Y_I_Top[minimal_x_idx])):
nr_points_within += 1
minimal_x_idx += 1
# Depends on definition if points outside of interpolation range should be count as outside or be dropped
percent_within = (nr_points_within * 100) / len(minimal_X)
df_result.loc[label] = [percent_within]
print(df_result)
Run Code Online (Sandbox Code Playgroud)
我认为并且我真的希望有更优雅的方法来实现它,尤其是最后的 for 循环。
我对其进行了一些测试,至少乍一看效果很好。对于你标记的,我得到了 71.8%(0,1,3)和 0,8%(2,1,3)。
我只是比较插值后的每一行。但此时你可以更进一步。例如,您可以获得样条插值系数,然后计算轨迹的交点。因此,您可以计算 x 轴上投影的百分比,或者实际上是落在其中的轨迹长度的百分比。也许有一个很好的误差估计。我希望这对我有一点帮助。
根据评论进行更详细的解释
首先,我在变量和解释中重命名了 Z 轴 Y,我希望这不会太令人困惑。使用 scipy 函数interp1d我对底部/顶部轨迹进行样条插值。基本上这意味着我根据底部和顶部轨迹的给定 X/Y 值对两个数学函数进行建模。这些函数返回底部或顶部的连续输出。对于每个 X 值,即使对于数据中未出现的 X 值,我也会从轨迹中获取 Y 值。这是通过所谓的样条插值来完成的。在数据中的每个 X/Y 值对之间计算一条线 (m * x +t)。您还可以使用关键字“cubic”来计算二阶多边形 (a * x^2 + b * x + c)。现在,通过这个模型,我可以查看底部和顶部轨迹在红色轨迹给出的 X 值处的值。
但这种方法有其局限性,这就是为什么我需要删除一些值。插值仅在数据集给定的 X 值的最小值和最大值之间定义。例如,如果红色轨迹的最小 X 值 x1 小于数据集中的底部轨迹,则我无法获得 x1 相应的 Y 值,因为底部轨迹的插值未在 x1 处定义。因此,我将自己限制在一个范围内,在该范围内,我知道每个轨迹,其中我的插值对于底部和顶部都有明确的定义。
PS: 这是我整个数据集的输出:
Percentage
(0, 1, 1) 3.427419
(0, 1, 2) 76.488396
(0, 1, 3) 71.802618
(0, 2, 1) 6.889564
(0, 2, 2) 16.330645
(0, 2, 3) 59.233098
(1, 1, 1) 13.373860
(1, 1, 2) 45.262097
(1, 1, 3) 91.084093
(1, 2, 1) 0.505051
(1, 2, 2) 1.010101
(1, 2, 3) 41.253792
(2, 1, 1) 4.853387
(2, 1, 2) 12.916246
(2, 1, 3) 0.808081
(2, 2, 1) 0.101112
(2, 2, 2) 0.708502
(2, 2, 3) 88.810484
Run Code Online (Sandbox Code Playgroud)