Skip to main content
 首页 » 编程设计

python之当函数应用于行时, Pandas 的 .groupby 函数出现奇怪的问题

2025年05月04日14del

我有一组 CSV 数据,它是 4203x37,我将其整形为 50436x4,以便找到每次记录的 12 组 3D 点之间的欧氏距离-步。这对我的实际数据不起作用,但奇怪的是,当我用随机数重新创建数据时它起作用了,代码如下......

这是我的实际数据的代码,它不起作用。

df_f_2_norm = df_f.loc[:,'Time':'label37'] # Select columns 
N = 12 # Nr of points 
 
# Drop label1 column for later use 
df_f_2_norm_time = df_f_2_norm['Time'] 
df_f_2_norm = df_f_2_norm.drop('Time',1) 
 
# Get shape of data frame 
shp = df_f_2_norm.shape 
 
# Use numpy.reshape to reshape the underlying data in the DataFrame 
df_f_2_norm = pd.DataFrame(df_f_2_norm.values.reshape(-1,3),columns=list('XYZ')) 
df_f_2_norm["Time"] = np.repeat(np.array(df_f_2_norm_time), N) # Number of points per time-label: 12 
 
# Find the Euclidean distance (2-norm) 
N_lim = int(0.5*N*(N-1))  
result_index = ['D{}'.format(tag) for tag in range(1,N_lim+1)] # Column labels 
two_norm = df_f_2_norm.groupby('Time')[["X", "Y", "Z"]].apply(lambda g: pd.Series(pdist(g), index=result_index)) 

现在,如果我们看一下 two_norm 的形状,它应该具有 4203x66 的形状,即 12 个点有 66 个欧氏距离,每个时间戳有4203,每行一个。

实际的答案实际上是这样的:AssertionError: Index length did not match values - 所以它不喜欢我给它的列标签。好吧,如果我们删除标签并改为执行

two_norm = df_f_2_norm.groupby('Time')[["X", "Y", "Z"]].apply(lambda g: pd.Series(pdist(g)) 

然后我们得到 (8307846,) 的形状 (print two_norm.shape) - 我不太明白这里发生了什么,但看起来它是甚至没有将所有结果堆叠在一起。

不过它变得更好了,因为下面的代码确实可以工作到第 1140 行,所以如果我们让

df_f_2_norm = df_f_2_norm[:1140] 

然后我们得到如下形状:(95,66)

在那之前这是正确的,但如果我们这样做了

df_f_2_norm = df_f_2_norm[:1152] 

而是给出:(6480,)

所以那里显然有些东西变成了梨形,但如果我们真的看看那个点周围的数据,似乎没有什么奇怪的。

             X         Y        Z   Time 
1127  -614.770   207.624  120.859  2.533 
1128   791.318   291.591   64.160  2.550 
1129   728.892   283.473 -207.306  2.550 
1130   939.871   251.387 -145.103  2.550 
1131   702.987   287.165  398.151  2.550 
1132   480.309   285.745  590.925  2.550 
1133   723.493   248.699  607.543  2.550 
1134   255.664   183.618 -108.176  2.550 
1135   -90.333   196.879 -261.102  2.550 
1136  -442.132   236.314 -419.216  2.550 
1137   133.428   216.805  242.896  2.550 
1138  -242.201   192.100  191.588  2.550 
1139  -616.844   210.060  123.202  2.550 
1140  -655.054  1390.084 -359.369  1.100 
1141  -726.517  1222.015 -590.799  1.100 
1142  -671.655  1146.959 -797.080  1.100 
1143  -762.048  1379.722    8.505  1.100 
1144  -981.748  1169.959   72.773  1.100 
1145 -1011.853   968.364  229.070  1.100 
1146  -778.290   827.571 -370.463  1.100 
1147  -761.608   460.835 -329.487  1.100 
1148  -815.330    77.501 -314.721  1.100 
1149  -925.764   831.944  -34.206  1.100 
1150 -1009.297   475.362  -73.077  1.100 
1151 -1193.310   139.839 -142.666  1.100 
1152  -631.630  1388.573 -353.642  1.117 
1153  -697.771  1234.274 -593.501  1.117 

所以这很奇怪。所以我试着用随机数来复制这个问题,但它一切正常,甚至是标签,这毫无意义......

import numpy as np 
import pandas as pd 
import string 
from scipy.spatial.distance import pdist, squareform 
# Computes the distance between m points using Euclidean distance (2-norm) 
# as the distance metric between the points. The points are arranged as m  
# n-dimensional row vectors in the matrix X. 
 
# Test data frame 
N = 12 # Nr of points 
col_ids = string.letters[:N] 
df = pd.DataFrame( 
      np.random.randn(4203, 3*N+1),  
      columns=['Time']+['{}_{}'.format(letter, coord) for letter in col_ids for coord in list('xyz')]) 
 
# Drop time column for later use 
df_time = df['Time'] 
df = df.drop('Time',1) 
 
print df.shape 
 
# Use numpy.reshape to reshape the underlying data in the DataFrame 
df = pd.DataFrame(df.values.reshape(-1,3), columns=list('XYZ')) 
df["Time"] = np.repeat(np.array(df_time), N) 
 
print df.shape 
 
# Find the Euclidean distance (2-norm) 
N_lim = int(0.5*N*(N-1)) 
result_index = ['D{}'.format(coord) for coord in range(1,N_lim+1)] 
two_norm = df.groupby('Time')[["X", "Y", "Z"]].apply(lambda g: pd.Series(pdist(g), index=result_index)) 
 
print two_norm.shape 

其中有输出(来自三个打印语句)

(4203, 36) 
(50436, 4) 
(4203, 66) 

如您所见,最终结果的形状完全符合预期。但是这两组数据之间确实没有什么不同(据我所知),除非数值差异不会对结果数据框的实际形状产生任何影响。

我错过了什么?

谢谢。


原始数据可在此处找到(本文第一部分中使用的数据):https://www.dropbox.com/sh/80f8ue4ffa4067t/Pntl5-gUW4

应该注意的是,在 dropbox 中找到的 .csv 文件是数据框 df_f_2_norm - 因此它不是原始数据,而是重新整形的版本(所以第一行代码在上面,不需要执行到这个状态,因为它已经被执行了)。

请您参考如下方法:

如果你运行下面的代码

df_f_2_norm.Time.value_counts() 

然后你会发现并不是所有的时间值都是12行。

这是输出:

1.333    492 
1.383    492 
1.317    492 
1.400    492 
1.467    492 
1.450    492 
1.483    492 
1.417    492 
1.500    492 
1.367    492 
1.350    492 
1.433    492 
1.533    480 
1.517    480 
1.550    468 
... 
4.800    12 
4.600    12 
4.750    12 
4.833    12 
4.667    12 
4.700    12 
4.650    12 
4.683    12 
4.633    12 
4.617    12 
4.817    12 
4.583    12 
4.733    12 
4.767    12 
4.783    12 
Length: 272, dtype: int64 

如果您想每 12 行对数据框进行分组,您可以:

import pandas as pd 
from scipy.spatial.distance import pdist, squareform 
 
df_f_2_norm = pd.read_csv("astrid_data.csv") 
g = np.repeat(np.arange(df_f_2_norm.shape[0]//12), 12) 
 
N = 12 
 
N_lim = int(0.5*N*(N-1))  
result_index = ['D{}'.format(tag) for tag in range(1,N_lim+1)] # Column labels 
two_norm = df_f_2_norm.groupby(g)[["X", "Y", "Z"]].apply(lambda g: pd.Series(pdist(g), index=result_index))