2017-10-25 11 views
1

浮動小数点値である可能性のあるタイムスタンプに従って、データリスト(現在はpandas.DataFrame)の平均をブロックする必要のあるアプリケーションがあります。例えば、私は0.3秒のグループに以下のdfを平均化する必要があるかもしれません:整数durationのためだけで正常に動作し浮動小数点数のグループ化

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 

+------+------+   +------+------+ 
| 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 | .. | 
| ... | ... | 
+------+------+ 

は現在、私は、次の(最低限の)ソリューションを使用していますブロックの端の浮動小数点値は間違った側に落ちることがあります。ブロックが適切に作成されているという単純なテストは、データが既に入っている(この例では0.1)同じdurationで平均化することです。これは入力を返すはずですが、しばしば入力しません。 (例えばx=.1*np.arange(1,20); (x-x[0])//.1)。)

私はこの方法でエラーが通常であることが分かっているLSB 1が低いので、仮の修正はgroupingの分子にnp.spacing(df['secs'])を追加することです。 (つまり、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をグループ化する(おそらくはより簡単な)アルゴリズムで同様の問題が発生しました。

+0

不正確な場合は、msまたはns(つまり、 'np.timedelta')? –

+0

@AndyHayden残念ながら、私はデータの書き方をあまり制御していません。しかし、(今のところ)それは常に1ミリ秒の整数倍であるので、私は '((...)* 1000).astype(int)'にすることができます。 floatが(ほとんど)不正確であることに気づいているので、一方(または両方)の方向に 'np.spacing'を埋め込むことで本当に保証はありませんか? – nivk

+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 

*主な問題は、フローティングポイントが常にあることを行っているということですそこリサンプル引数を通じてこれを設定する方法であるべきだが、彼らは動作していないようです...

+0

これはすばらしく、私はmsへの変換が一番良いと思います。TBH、以前は「timedelta」という言葉を聞いたことはありませんでしたが、本当に素晴らしいツールのようです。 – nivk

+0

@nivkもしあなたがうまくいけば "受け入れ"を検討してください:) https://stackoverflow.com/help/someone-answers –

+0

私は一般的にすぐに受け入れるのが好きではありません(特に低視野の質問)。私は例外を作ることができます。 (何が126kで十分ではないのですか?) – nivk

関連する問題