Python 将函数应用于pandas,';循环';在整个数据帧上按列显示
下面是一个测试示例,演示我试图实现的目标。这是一个玩具数据框:Python 将函数应用于pandas,';循环';在整个数据帧上按列显示,python,numpy,pandas,Python,Numpy,Pandas,下面是一个测试示例,演示我试图实现的目标。这是一个玩具数据框: df = pd.DataFrame(np.random.randn(10,7),index=range(1,11),columns=headers) 给 Time A_x A_y A_z B_x B_y B_z 1 -0.075509 -0.123527 -0.547239 -0.453707 -0.969796 0.248761 1.36961
df = pd.DataFrame(np.random.randn(10,7),index=range(1,11),columns=headers)
给
Time A_x A_y A_z B_x B_y B_z
1 -0.075509 -0.123527 -0.547239 -0.453707 -0.969796 0.248761 1.369613
2 -0.206369 -0.112098 -1.122609 0.218538 -0.878985 0.566872 -1.048862
3 -0.194552 0.818276 -1.563931 0.097377 1.641384 -0.766217 -1.482096
4 0.502731 0.766515 -0.650482 -0.087203 -0.089075 0.443969 0.354747
5 1.411380 -2.419204 -0.882383 0.005204 -0.204358 -0.999242 -0.395236
6 1.036695 1.115630 0.081825 -1.038442 0.515798 -0.060016 2.669702
7 0.392943 0.226386 0.039879 0.732611 -0.073447 1.164285 1.034357
8 -1.253264 0.389148 0.158289 0.440282 -1.195860 0.872064 0.906377
9 -0.133580 -0.308314 -0.839347 -0.517989 0.652120 0.477232 -0.391767
10 0.623841 0.473552 0.059428 0.726088 -0.593291 -3.186297 -0.846863
我想做的只是计算每个标题(A和B)的向量长度,在这种情况下,每个索引的向量长度除以Time
列。因此,这个函数需要是np.sqrt(A_x^2+A_y^2+A_z^2)
,当然B也是如此。也就是说,我希望计算每行的速度,但三列构成一个速度结果
我曾尝试使用df.groupby
和df.filter
在列上循环,但我无法真正让它工作,因为我根本不知道如何将相同的函数有效地应用于数据帧的块,一次完成(显然是为了避免在行上循环)。我已经试过了
df = df.apply(lambda x: np.sqrt(x.dot(x)), axis=1)
当然,这是可行的,但只有当输入数据帧的列数(3)正确时,如果点积在整行上计算,而不是在我想要的三列的块中计算(因为这是对应于三维标记坐标的圈数)
这就是我最终试图通过上面的例子得到的结果(下面的数组只是用随机数填充的,而不是我试图计算的实际速度-只是为了显示我试图实现的形状):
我的实际数据是50000 x 36(因此有12个标记,带有x、y、z坐标),我想一次计算速度,以避免迭代(如果可能的话)。还有一个长度相同的时间列(50000x1)
你是怎么做到的
谢谢,阿斯特里德,这是一个可能的开始
筛选出与特定向量对应的列名。比如说
In [20]: filter(lambda x: x.startswith("A_"),df.columns)
Out[20]: ['A_x', 'A_y', 'A_z']
从数据框中选择这些列
In [22]: df[filter(lambda x: x.startswith("A_"),df.columns)]
Out[22]:
A_x A_y A_z
1 -0.123527 -0.547239 -0.453707
2 -0.112098 -1.122609 0.218538
3 0.818276 -1.563931 0.097377
4 0.766515 -0.650482 -0.087203
5 -2.419204 -0.882383 0.005204
6 1.115630 0.081825 -1.038442
7 0.226386 0.039879 0.732611
8 0.389148 0.158289 0.440282
9 -0.308314 -0.839347 -0.517989
10 0.473552 0.059428 0.726088
因此,使用这种技术可以得到3列的块。比如说
column_initials = ["A","B"]
for column_initial in column_initials:
df["Velocity_"+column_initial]=df[filter(lambda x: x.startswith(column_initial+"_"),df.columns)].apply(lambda x: np.sqrt(x.dot(x)), axis=1)/df.Time
In [32]: df[['Velocity_A','Velocity_B']]
Out[32]:
Velocity_A Velocity_B
1 -9.555311 -22.467965
2 -5.568487 -7.177625
3 -9.086257 -12.030091
4 2.007230 1.144208
5 1.824531 0.775006
6 1.472305 2.623467
7 1.954044 3.967796
8 -0.485576 -1.384815
9 -7.736036 -6.722931
10 1.392823 5.369757
我得到的答案和你的不一样。但是,我借用了你的df.apply(lambda x:np.sqrt(x.dot(x)),axis=1)
,并假设它是正确的
希望这能有所帮助。您的计算比Panda ish更简单,我的意思是,如果您认为您的数据帧仅仅是一个大数组,那么计算可以简洁地表达出来,而当您试图将数据帧与melt、groupby等进行争论时,解决方案(至少是我提出的解决方案)更复杂 整个计算基本上可以用一行表示:
np.sqrt((arr**2).reshape(arr.shape[0],-1,3).sum(axis=-1))/times[:,None]
下面是一个简单的方法:
import numpy as np
import pandas as pd
import io
content = '''
Time A_x A_y A_z B_x B_y B_z
-0.075509 -0.123527 -0.547239 -0.453707 -0.969796 0.248761 1.369613
-0.206369 -0.112098 -1.122609 0.218538 -0.878985 0.566872 -1.048862
-0.194552 0.818276 -1.563931 0.097377 1.641384 -0.766217 -1.482096
0.502731 0.766515 -0.650482 -0.087203 -0.089075 0.443969 0.354747
1.411380 -2.419204 -0.882383 0.005204 -0.204358 -0.999242 -0.395236
1.036695 1.115630 0.081825 -1.038442 0.515798 -0.060016 2.669702
0.392943 0.226386 0.039879 0.732611 -0.073447 1.164285 1.034357
-1.253264 0.389148 0.158289 0.440282 -1.195860 0.872064 0.906377
-0.133580 -0.308314 -0.839347 -0.517989 0.652120 0.477232 -0.391767
0.623841 0.473552 0.059428 0.726088 -0.593291 -3.186297 -0.846863'''
df = pd.read_table(io.BytesIO(content), sep='\s+', header=True)
arr = df.values
times = arr[:,0]
arr = arr[:,1:]
result = np.sqrt((arr**2).reshape(arr.shape[0],-1,3).sum(axis=-1))/times[:,None]
result = pd.DataFrame(result, columns=['Velocity_%s'%(x,) for x in list('AB')])
print(result)
产生
Velocity_A Velocity_B
0 -9.555311 -22.467965
1 -5.568487 -7.177625
2 -9.086257 -12.030091
3 2.007230 1.144208
4 1.824531 0.775006
5 1.472305 2.623467
6 1.954044 3.967796
7 -0.485576 -1.384815
8 -7.736036 -6.722931
9 1.392823 5.369757
由于您的实际数据帧具有形状(50000、36),因此选择快速方法可能很重要。以下是一个基准:
import numpy as np
import pandas as pd
import string
N = 12
col_ids = string.letters[:N]
df = pd.DataFrame(
np.random.randn(50000, 3*N+1),
columns=['Time']+['{}_{}'.format(letter, coord) for letter in col_ids
for coord in list('xyz')])
def using_numpy(df):
arr = df.values
times = arr[:,0]
arr = arr[:,1:]
result = np.sqrt((arr**2).reshape(arr.shape[0],-1,3).sum(axis=-1))/times[:,None]
result = pd.DataFrame(result, columns=['Velocity_%s'%(x,) for x in col_ids])
return result
def using_loop(df):
results = pd.DataFrame(index=df.index) # the result container
for id in col_ids:
results['Velocity_'+id] = np.sqrt((df.filter(regex=id+'_')**2).sum(axis=1))/df.Time
return results
使用:
我至少会在标记标识符上做一个循环,但别担心,这是一个非常快速的循环,它只确定过滤器模式以获得正确的列:
df = pd.DataFrame(np.random.randn(10,7), index=range(1,11), columns='Time A_x A_y A_z B_x B_y B_z'.split())
col_ids = ['A', 'B'] # I guess you can create that one easily
results = pd.DataFrame(index=df.index) # the result container
for id in col_ids:
results['Velocity_'+id] = np.sqrt((df.filter(regex=id+'_')**2).sum(axis=1))/df.Time
一行…拆分为多行以提高可读性:
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame(
np.random.randn(10,7),
index=range(1,11),
columns='Time A_x A_y A_z B_x B_y B_z'.split()
)
result = df\
.loc[:, df.columns.values!='Time']\
.T\
.groupby(lambda x: x[0])\
.apply(lambda x: np.sqrt((x ** 2).sum()))\
.T\
.apply(lambda x: x / df['Time'])
print result
A B
1 1.404626 1.310639
2 -2.954644 -10.874091
3 3.479836 6.105961
4 3.885530 2.244544
5 0.995012 1.434228
6 11.278208 11.454466
7 -1.209242 -1.281165
8 -5.175911 -5.905070
9 11.889318 16.758958
10 -0.978014 -0.590767
注:我有点沮丧,我需要在两个转置抛出。我就是无法让groupby
和apply
很好地使用axis=1
。如果有人能告诉我怎么做,我将非常感激。这里的诀窍是知道当您调用groupby(lambda x:f(x))
时,x
是每行的索引值。所以groupby(lambda x:x[0])
按行索引的第一个字母分组。进行换位后,这是A
或B
好的,没有更多的转置:
result = df\
.loc[:, df.columns!='Time']\
.groupby(lambda x: x[0], axis=1)\
.apply(lambda x: np.sqrt((x**2).sum(1)))\
.apply(lambda x: x / df['Time'])
print result
A B
1 1.404626 1.310639
2 -2.954644 -10.874091
3 3.479836 6.105961
4 3.885530 2.244544
5 0.995012 1.434228
6 11.278208 11.454466
7 -1.209242 -1.281165
8 -5.175911 -5.905070
9 11.889318 16.758958
10 -0.978014 -0.590767
啊,我道歉。我的解决方案与你的非常相似,只是你没有使用过滤函数的'regex'参数。实际的数字只是随机的,只是为了显示最终的产品应该是什么样子,或者至少我希望它是什么样子。非常感谢。我根本不知道你能这样做。谢谢
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame(
np.random.randn(10,7),
index=range(1,11),
columns='Time A_x A_y A_z B_x B_y B_z'.split()
)
result = df\
.loc[:, df.columns.values!='Time']\
.T\
.groupby(lambda x: x[0])\
.apply(lambda x: np.sqrt((x ** 2).sum()))\
.T\
.apply(lambda x: x / df['Time'])
print result
A B
1 1.404626 1.310639
2 -2.954644 -10.874091
3 3.479836 6.105961
4 3.885530 2.244544
5 0.995012 1.434228
6 11.278208 11.454466
7 -1.209242 -1.281165
8 -5.175911 -5.905070
9 11.889318 16.758958
10 -0.978014 -0.590767
result = df\
.loc[:, df.columns!='Time']\
.groupby(lambda x: x[0], axis=1)\
.apply(lambda x: np.sqrt((x**2).sum(1)))\
.apply(lambda x: x / df['Time'])
print result
A B
1 1.404626 1.310639
2 -2.954644 -10.874091
3 3.479836 6.105961
4 3.885530 2.244544
5 0.995012 1.434228
6 11.278208 11.454466
7 -1.209242 -1.281165
8 -5.175911 -5.905070
9 11.889318 16.758958
10 -0.978014 -0.590767