在 pandas Intervalindex 中查找匹配间隔

Posted

技术标签:

【中文标题】在 pandas Intervalindex 中查找匹配间隔【英文标题】:Finding matching interval(s) in pandas Intervalindex 【发布时间】:2018-03-04 00:47:09 【问题描述】:

在 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]

有没有更好的方法来做到这一点?

【问题讨论】:

我不知道,你的方式有问题吗? @Zero 这似乎是一个有用的功能,所以我认为会有类似的东西。列表理解的唯一问题是循环;-/ @Bharathshetty 我是菜鸟。我不知道什么是好什么是坏! @cᴏʟᴅsᴘᴇᴇᴅ 为什么 get_loc 不适用于日期时间 @Bharath 它应该......我对这个 API 的经验不够,无法告诉你原因:( 【参考方案1】:

使用 NumPy

import numpy as np
data = [(893.1516130000001, 903.9187099999999),
         (882.384516, 893.1516130000001),
         (817.781935, 828.549032)]    
q = 900
# The next line broadcast q and tell if q is within the intervals/ranges defined in data (using numpy)
np.logical_xor(*(np.array(data) - q > 0).transpose())

【讨论】:

尝试添加一些上下文来提高答案质量。 EOR。【参考方案2】:

如果您正在寻找速度,您可以使用 idx 的左右,即从范围中获取下限和上限,然后检查数字是否在边界之间,即

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

或者

[(900 > idx.left) & (900 <= idx.right)]
[对,错,错]

对于小数据

%%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

此方法胜过您的大数据解决方案。

【讨论】:

我认为它们还没有记录,但你可以看到代码github.com/pandas-dev/pandas/blob/v0.20.3/pandas/core/indexes/… Jeff 是 pandas 的核心开发者之一。他的话就是法律......字面意思......但感谢您的理解:-)【参考方案3】:

如果您对性能感兴趣,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])

【讨论】:

谢谢!这真是太神奇了。所以,正如我所理解的,get_loc 构建了一个内部缓存的树,以便以后更快地搜索? 另外,您提到它们可以相互转换。第二个输出究竟如何映射到第一个? 是的,内部构建了一个IntervalTree,这是索引的方式;我们通常为其他索引类型构建一个哈希表来进行查找,但这种结构在这里产生了很多好处。更新了如何在索引器之间进行转换。【参考方案4】:

你可以使用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

结论:

理解是赢家,但在大量数据上并没有那么明显。

【讨论】:

感谢您的回答,但您能向我证明地图比列表理解更好吗?如果你不介意的话,我很想看看这方面的时间安排!还有一些大数据。 理解比较快:) 非常令人失望。我喜欢你的回答。不过,我很想看看时间安排。

以上是关于在 pandas Intervalindex 中查找匹配间隔的主要内容,如果未能解决你的问题,请参考以下文章

pandas数据分割pd.cut使用总结

如何将inf修改为索引

API 函数,我怎么在MSDN中查不到!

adb devices中查不到iTools android模拟器

navicat中查重并删除

sap系统中查工资明细表命令