3

设置

我有一个包含三列的 DataFrame:

  • “类别”包含真假,我已经df.groupby('Category')按这些值分组。
  • “时间”包含记录值的时间戳(以秒为单位)
  • “值”包含值本身。

在每个时间实例中,记录两个值:一个具有类别“真”,另一个具有类别“假”。

滚动申请问题

在每个类别组中,我想计算一个数字并将其存储在每次的列结果中。结果是时间介于 1 和 3 之间的值的百分比。t-60t

完成此操作的最简单方法可能是通过 计算该时间间隔内的值的总数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

find的正确rolling_apply()调用是grp['in_interval']什么?

4

2 回答 2

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)

所以 DataFramedf看起来像这样:

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

现在我们将使用我们的 DataFramedf和 group by 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是假的):

   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 列,并为每个 time应用一个函数。这是通过以下方式完成的applymap

def toeach_category(subf):
    result = subf[['Time']].applymap(percentage)

该函数percentage将时间值作为输入,并返回一个值作为输出。该值将是值在 1 和 3 之间的行的分数。applymap非常严格:percentage不能接受任何其他参数。

给定一个 time t,我们可以使用以下方法从中选择时间在半开区间内的Values :subf(t-60, t]ix

subf.ix[(t-60 < subf['Time']) & (subf['Time'] <= t), 'Value']

因此,我们可以Values通过应用找到介于 1 和 3 之间的百分比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,并且我们不允许将subftopercentage作为参数传递(同样,因为applymap非常严格)。

那么我们如何摆脱这种困境呢?解决方案是定义percentageinside toeach_category。Python 的作用域规则说,subf首先在 Local 作用域中查找裸名称 like,然后是 Enclosure 作用域、Global 作用域,最后是 Builtin 作用域。当percentage(t)被调用并且 Python 遇到 时subf,Python 首先在 Local 范围内查找 的值subf。由于subf不是 中的局部变量percentage,Python 在函数的封闭范围内查找它toeach_category。它在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
于 2013-03-18T21:52:32.477 回答
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))

然后使用函数名作为rolling_applyfor 循环中的参数:

grp['Result'] = pd.rolling_apply(grp['Value'], 60, between_1_3_perc)
于 2013-03-18T21:55:56.633 回答