web-dev-qa-db-ja.com

pandas Intervalindex)で一致する間隔を見つける

0.20の新しいIntervalindexと呼ばれるこの興味深いAPIがあり、間隔のインデックスを作成できます。

いくつかのサンプルデータが与えられた場合:

data = [(893.1516130000001, 903.9187099999999),
 (882.384516, 893.1516130000001),
 (817.781935, 828.549032)]

次のようにインデックスを作成できます。

idx = pd.IntervalIndex.from_tuples(data)

print(idx)
IntervalIndex([(893.151613, 903.91871], (882.384516, 893.151613], (817.781935, 828.549032]]
              closed='right',
              dtype='interval[float64]')

Intervalsの興味深い特性は、inを使用して間隔チェックを実行できることです。

print(y[-1])
Interval(817.78193499999998, 828.54903200000001, closed='right')

print(820 in y[-1])
True

print(1000 in y[-1])
False

この操作をインデックス全体に適用する方法を知りたいのですが。たとえば、いくつかの数が与えられた900、この数値が収まる間隔のブールマスクを取得するにはどうすればよいですか?

私は考えることができます:

m = [900 in y for y in idx]
print(m)
[True, False, False]

これを行うためのより良い方法はありますか?

12
cs95

パフォーマンスに関心がある場合は、IntervalIndexが検索用に最適化されています。 .get_locまたは.get_indexerを使用すると、最初の使用時に構築される、内部で構築されたIntervalTree(バイナリツリーなど)が使用されます。

In [29]: idx = pd.IntervalIndex.from_tuples(data*10000)

In [30]: %timeit -n 1 -r 1 idx.map(lambda x: 900 in x)
92.8 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

In [40]: %timeit -n 1 -r 1 idx.map(lambda x: 900 in x)
42.7 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

# construct tree and search
In [31]: %timeit -n 1 -r 1 idx.get_loc(900)
4.55 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

# subsequently
In [32]: %timeit -n 1 -r 1 idx.get_loc(900)
137 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

# for a single indexer you can do even better (note that this is
# dipping into the impl a bit
In [27]: %timeit np.arange(len(idx))[(900 > idx.left) & (900 <= idx.right)]
203 µs ± 1.55 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

.get_loc()はインデクサーを返すことに注意してください(これは実際にはブール配列よりも便利ですが、相互に変換可能です)。

In [38]: idx.map(lambda x: 900 in x)
    ...: 
Out[38]: 
Index([ True, False, False,  True, False, False,  True, False, False,  True,
       ...
       False,  True, False, False,  True, False, False,  True, False, False], dtype='object', length=30000)

In [39]: idx.get_loc(900)
    ...: 
Out[39]: array([29997,  9987, 10008, ..., 19992, 19989,     0])

ブール配列を返すと、インデクサーの配列に変換されます

In [5]: np.arange(len(idx))[idx.map(lambda x: 900 in x).values.astype(bool)]
Out[5]: array([    0,     3,     6, ..., 29991, 29994, 29997])

これは、.get_loc()と.get_indexer()が返すものです。

In [6]: np.sort(idx.get_loc(900))
Out[6]: array([    0,     3,     6, ..., 29991, 29994, 29997])
16
Jeff

mapを使用できます:

_idx.map(lambda x: 900 in x)
#Index([True, False, False], dtype='object')
_

タイミング:

_%timeit [900 in y for y in idx]
#100000 loops, best of 3: 3.76 µs per loop

%timeit idx.map(lambda x: 900 in x)
#10000 loops, best of 3: 48.7 µs per loop

%timeit map(lambda x: 900 in x, idx)
#100000 loops, best of 3: 4.95 µs per loop
_

明らかに、理解は最速ですが、組み込みのmapはそれほど遅れることはありません。

より多くのデータを導入しても結果は均一になります。正確には、10K倍のデータです。

_%timeit [900 in y for y in idx]
#10 loops, best of 3: 26.8 ms per loop

%timeit idx.map(lambda x: 900 in x)
#10 loops, best of 3: 30 ms per loop

%timeit map(lambda x: 900 in x, idx)
#10 loops, best of 3: 29.5 ms per loop
_

ご覧のとおり、組み込みのmap.map()に非常に近いので、-10倍以上のデータで何が起こるかを見てみましょう。

_%timeit [900 in y for y in idx]
#1 loop, best of 3: 270 ms per loop

%timeit idx.map(lambda x: 900 in x)
#1 loop, best of 3: 299 ms per loop

%timeit map(lambda x: 900 in x, idx)
#1 loop, best of 3: 291 ms per loop
_

結論:

理解力が勝者ですが、大量のデータではそれほど明確ではありません。

3
zipa

速度を探している場合は、idxの左右を使用できます。つまり、範囲から下限と上限を取得してから、数値が境界の間にあるかどうかを確認します。

list(lower <= 900 <= upper for (lower, upper) in Zip(idx.left,idx.right))

または

[(900 > idx.left) & (900 <= idx.right)]
 [True、False、False] 

小さなデータの場合

%%timeit
list(lower <= 900 <= upper for (lower, upper) in Zip(idx.left,idx.right))
100000 loops, best of 3: 11.26 µs per loop

%%timeit
[900 in y for y in idx]
100000 loops, best of 3: 9.26 µs per loop

大きなデータの場合

idx = pd.IntervalIndex.from_tuples(data*10000)

%%timeit
list(lower <= 900 <= upper for (lower, upper) in Zip(idx.left,idx.right))
10 loops, best of 3: 29.2 ms per loop

%%timeit
[900 in y for y in idx]
10 loops, best of 3: 64.6 ms per loop

この方法は、大規模なデータのソリューションに勝るものです。

3
Bharath M