对浮点数进行分组

时间:2017-10-25 00:58:53

标签: python pandas numpy floating-point

我有一个应用程序,我需要根据时间戳阻塞平均数据列表(当前在pandas.DataFrame),时间戳可能是浮点值。例如,我可能需要将以下df平均分为0.3秒组:

+------+------+         +------+------+
| secs |  A   |         | secs |  A   |
+------+------+         +------+------+
| 0.1  |  ..  |         | 0.3  |  ..  | <-- avg of 0.1, 0.2, 0.3
| 0.2  |  ..  |   -->   | 0.6  |  ..  | <-- avg of 0.4, 0.5, 0.6
| 0.3  |  ..  |         | ...  | ...  | <-- etc
| 0.4  |  ..  |         +------+------+
| 0.5  |  ..  |
| 0.6  |  ..  |
| ...  | ...  |
+------+------+

目前我正在使用以下(最小)解决方案:

import pandas as pd
import numpy as np

def block_avg ( df : pd.DataFrame, duration : float ) -> pd.DataFrame:
    grouping = (df['secs'] - df['secs'][0]) // duration
    df = df.groupby( grouping, as_index=False ).mean()
    df['secs'] = duration * np.arange(1,1+len(df))
    return df

对于整数duration来说效果很好,但是块边缘的浮点值可能会出错。正确创建块的简单测试是平均数据已经存在的duration(在此示例中为0.1)。这应该返回输入,但通常不会。 (例如x=.1*np.arange(1,20); (x-x[0])//.1)。)

我发现此方法的错误通常是LSB为1低,因此暂时的解决方法是将np.spacing(df['secs'])添加到grouping中的分子。 (也就是说,x=.1*np.arange(1,20); all( (x-x[0]+np.spacing(x)) // .1 == np.arange(19) )会返回True。)

但是,我担心这不是一个强有力的解决方案。是否有更好或更优先的方法来组合通过上述测试的花车?

我遇到了类似的问题(可能更直接)算法,该算法使用x[ (duration*i < x) & (x <= duration*(i+1)) ]进行分组,并在适当的范围内循环i

1 个答案:

答案 0 :(得分:2)

要特别小心(浮动不准确)我会在做groupby之前提前完成:

In [11]: np.round(300 + df.secs * 1000).astype(int) // 300
Out[11]:
0    1
1    1
2    1
3    2
4    2
5    2
Name: secs, dtype: int64

In [12]: (np.round(300 + df.secs * 1000).astype(int) // 300) * 0.3
Out[12]:
0    0.3
1    0.3
2    0.3
3    0.6
4    0.6
5    0.6
Name: secs, dtype: float64

In [13]: df.groupby(by=(np.round(300 + df.secs * 1000).astype(int) // 300) * 0.3)["A"].sum()
Out[13]:
secs
0.3    1.753843
0.6    2.687098
Name: A, dtype: float64

我更喜欢使用timedelta:

In [21]: s = pd.to_timedelta(np.round(df["secs"], 1), unit="S")

In [22]: df["secs"] = pd.to_timedelta(np.round(df["secs"], 1), unit="S")

In [23]: df.groupby(pd.Grouper(key="secs", freq="0.3S")).sum()
Out[23]:
                        A
secs
00:00:00         1.753843
00:00:00.300000  2.687098

或使用resample

In [24]: res = df.set_index("secs").resample("300ms").sum()

In [25]: res
Out[25]:
                        A
secs
00:00:00         1.753843
00:00:00.300000  2.687098

您可以设置索引以更正标签*

In [26]: res.index += np.timedelta64(300, "ms")

In [27]: res
Out[27]:
                        A
secs
00:00:00.300000  1.753843
00:00:00.600000  2.687098

*应该有一种方法通过重新采样参数设置它,但它们似乎不起作用......