我有一个包含三列的DataFrame:
df.groupby('Category')
按这些值进行分组。在每个时间实例中,记录两个值:一个具有“True”类别,另一个具有“False”类别。
在每个类别组中,我想计算一个数字并将其存储在每次结果列中。结果时间t-60
和t
之间的值在1到3之间的百分比。
实现此目的的最简单方法可能是通过rolling_count
计算该时间间隔内的值总数,然后执行rolling_apply
以仅计算该间隔中介于1和3之间的值
到目前为止,这是我的代码:
groups = df.groupby(['Category'])
for key, grp in groups:
grp = grp.reindex(grp['Time']) # reindex by time so we can count with rolling windows
grp['total'] = pd.rolling_count(grp['Value'], window=60) # count number of values in the last 60 seconds
grp['in_interval'] = ? ## Need to count number of values where 1<v<3 in the last 60 seconds
grp['Result'] = grp['in_interval'] / grp['total'] # percentage of values between 1 and 3 in the last 60 seconds
查找rolling_apply()
的正确grp['in_interval']
调用是什么?
答案 0 :(得分:7)
让我们来看一个例子:
import pandas as pd
import numpy as np
np.random.seed(1)
def setup(regular=True):
N = 10
x = np.arange(N)
a = np.arange(N)
b = np.arange(N)
if regular:
timestamps = np.linspace(0, 120, N)
else:
timestamps = np.random.uniform(0, 120, N)
df = pd.DataFrame({
'Category': [True]*N + [False]*N,
'Time': np.hstack((timestamps, timestamps)),
'Value': np.hstack((a,b))
})
return df
df = setup(regular=False)
df.sort(['Category', 'Time'], inplace=True)
所以DataFrame df
看起来像这样:
In [4]: df
Out[4]:
Category Time Value Result
12 False 0.013725 2 1.000000
15 False 11.080631 5 0.500000
14 False 17.610707 4 0.333333
16 False 22.351225 6 0.250000
13 False 36.279909 3 0.400000
17 False 41.467287 7 0.333333
18 False 47.612097 8 0.285714
10 False 50.042641 0 0.250000
19 False 64.658008 9 0.125000
11 False 86.438939 1 0.333333
2 True 0.013725 2 1.000000
5 True 11.080631 5 0.500000
4 True 17.610707 4 0.333333
6 True 22.351225 6 0.250000
3 True 36.279909 3 0.400000
7 True 41.467287 7 0.333333
8 True 47.612097 8 0.285714
0 True 50.042641 0 0.250000
9 True 64.658008 9 0.125000
1 True 86.438939 1 0.333333
现在,复制@herrfz,让我们定义
def between(a, b):
def between_percentage(series):
return float(len(series[(a <= series) & (series < b)])) / float(len(series))
return between_percentage
between(1,3)
是一个函数,它将一个Series作为输入,并返回其半开区间[1,3)
中元素的分数。例如,
In [9]: series = pd.Series([1,2,3,4,5])
In [10]: between(1,3)(series)
Out[10]: 0.4
现在我们将采用我们的DataFrame,df
和Category
分组:
df.groupby(['Category'])
对于groupby对象中的每个组,我们将要应用一个函数:
df['Result'] = df.groupby(['Category']).apply(toeach_category)
函数toeach_category
将(子)DataFrame作为输入,并返回DataFrame作为输出。整个结果将分配到名为df
的新Result
列。
现在究竟必须toeach_category
做什么?如果我们这样写toeach_category
:
def toeach_category(subf):
print(subf)
然后我们看到每个subf
都是一个DataFrame,例如这个(当Category
为False时):
Category Time Value Result
12 False 0.013725 2 1.000000
15 False 11.080631 5 0.500000
14 False 17.610707 4 0.333333
16 False 22.351225 6 0.250000
13 False 36.279909 3 0.400000
17 False 41.467287 7 0.333333
18 False 47.612097 8 0.285714
10 False 50.042641 0 0.250000
19 False 64.658008 9 0.125000
11 False 86.438939 1 0.333333
我们想要使用Times列,并且每次,应用一个函数。这是通过applymap
:
def toeach_category(subf):
result = subf[['Time']].applymap(percentage)
函数percentage
将时间值作为输入,并返回一个值作为输出。该值将是值为1到3的行的分数。applymap
非常严格:percentage
不能接受任何其他参数。
如果时间为t
,我们可以使用Value
方法从subf
中选择时间处于半开区间(t-60, t]
的{{1}} :
ix
因此,我们可以通过应用subf.ix[(t-60 < subf['Time']) & (subf['Time'] <= t), 'Value']
找到1到3之间Values
的百分比:
between(1,3)
现在请记住,我们需要一个函数between(1,3)(subf.ix[(t-60 < subf['Time']) & (subf['Time'] <= t), 'Value'])
,它将percentage
作为输入,并将上面的表达式作为输出返回:
t
但请注意,def percentage(t):
return between(1,3)(subf.ix[(t-60 < subf['Time']) & (subf['Time'] <= t), 'Value'])
取决于percentage
,我们不允许将subf
作为参数传递给subf
(同样,因为percentage
是applymap
非常严格)。
那么我们如何摆脱这种干扰呢?解决方案是在percentage
内定义toeach_category
。 Python的范围规则表明,首先在Local范围内查找像subf
这样的简单名称,然后是Enclosing范围,Global范围,最后是在Builtin范围内。调用percentage(t)
并且Python遇到subf
时,Python首先在Local范围内查找subf
的值。由于subf
不是percentage
中的局部变量,因此Python会在函数toeach_category
的Enclosing范围内查找它。它在那里找到subf
。完善。这正是我们所需要的。
现在我们有了toeach_category
函数:
def toeach_category(subf):
def percentage(t):
return between(1, 3)(
subf.ix[(t - 60 < subf['Time']) & (subf['Time'] <= t), 'Value'])
result = subf[['Time']].applymap(percentage)
return result
全部放在一起,
import pandas as pd
import numpy as np
np.random.seed(1)
def setup(regular=True):
N = 10
x = np.arange(N)
a = np.arange(N)
b = np.arange(N)
if regular:
timestamps = np.linspace(0, 120, N)
else:
timestamps = np.random.uniform(0, 120, N)
df = pd.DataFrame({
'Category': [True] * N + [False] * N,
'Time': np.hstack((timestamps, timestamps)),
'Value': np.hstack((a, b))
})
return df
def between(a, b):
def between_percentage(series):
return float(len(series[(a <= series) & (series < b)])) / float(len(series))
return between_percentage
def toeach_category(subf):
def percentage(t):
return between(1, 3)(
subf.ix[(t - 60 < subf['Time']) & (subf['Time'] <= t), 'Value'])
result = subf[['Time']].applymap(percentage)
return result
df = setup(regular=False)
df.sort(['Category', 'Time'], inplace=True)
df['Result'] = df.groupby(['Category']).apply(toeach_category)
print(df)
产量
Category Time Value Result
12 False 0.013725 2 1.000000
15 False 11.080631 5 0.500000
14 False 17.610707 4 0.333333
16 False 22.351225 6 0.250000
13 False 36.279909 3 0.200000
17 False 41.467287 7 0.166667
18 False 47.612097 8 0.142857
10 False 50.042641 0 0.125000
19 False 64.658008 9 0.000000
11 False 86.438939 1 0.166667
2 True 0.013725 2 1.000000
5 True 11.080631 5 0.500000
4 True 17.610707 4 0.333333
6 True 22.351225 6 0.250000
3 True 36.279909 3 0.200000
7 True 41.467287 7 0.166667
8 True 47.612097 8 0.142857
0 True 50.042641 0 0.125000
9 True 64.658008 9 0.000000
1 True 86.438939 1 0.166667
答案 1 :(得分:2)
如果我正确理解您的问题陈述,如果您仅为了计算百分比而使用它,则可能会跳过rolling count
。 rolling_apply
将执行聚合的函数作为参数,即将数组作为输入并将数字作为输出返回的函数。
考虑到这一点,我们首先定义一个函数:
def between_1_3_perc(x):
# pandas Series is basically a numpy array, we can do boolean indexing
return float(len(x[(x > 1) & (x < 3)])) / float(len(x))
然后在for循环中使用函数名作为rolling_apply
的参数:
grp['Result'] = pd.rolling_apply(grp['Value'], 60, between_1_3_perc)