元组列表(开始、结束)到索引范围(将 pandas.IntervalArray 转换为 numpy 数组?)

List of tuples (begin, end) to index range (convert a pandas.IntervalArray to a numpy array?)

目标

我有一个值数组和一个元组列表,表示需要从该数组中选择哪些索引。 (想想表示音频数组的哪一部分是语音的元组。)我正在考虑使用选择掩码:

import numpy as np

# sample data
arr = np.array([.3, .4, .5, -.2, -.1, .7, .9])
selection_idx = [(0, 3), (5,7)]

# unknown: how to efficiently selection_idx -> mask?
mask = [0, 1, 2, 5, 6]  # or
mask = [True, True, True, False, False, True, True]

# desired result 1
arr[mask]
# Out: array([0.3, 0.4, 0.5, 0.7, 0.9])

Pandas掩码的IntervalArray方法

Numpy 本身限制为 numpy.arange (from what I could find) to generate regular Interval sequences. Pandas however has the pandas.IntervalArray object, which can be created with useful functions such as .from_tuples。 用代码术语来说就是:

import pandas as pd

pd.arrays.IntervalArray.from_tuples(selection)
# Out:
# <IntervalArray>
# [(0, 3], (5, 7]]
# Length: 2, closed: right, dtype: interval[int64]

问题

这可以解决您的问题,尽管我相信 Numpy 中可能存在我不知道的语法:

from itertools import chain
arr = np.array([.3, .4, .5, -.2, -.1, .7, .9])
selection_idx = [(0, 3), (5,7)]
m = list(chain.from_iterable(range(a,b) for a,b in selection_idx))

print(m)
# [0, 1, 2, 5, 6]

arr[m]
array([0.3, 0.4, 0.5, 0.7, 0.9])

基本上,它通过解压每个元组并使用 itertool 的 chain.from_iterable 将所有内容合并为一个来获取列表。剩下的就是使用 numpy 的索引来获取你的结果。

请注意,如果您有布尔值,则可以使用 numpy 的 compress 来获取输出:

mask = [True, True, True, False, False, True, True]
np.compress(mask, arr)

一个想法是使用带有扁平化的列表理解:

mask = [c for a,b in selection_idx for c in range(a,b)]
print(arr[mask])
[0.3 0.4 0.5 0.7 0.9]

很容易加入各自的 aranges:

In [14]: np.r_[0:3,5:7]                                                                                                    
Out[14]: array([0, 1, 2, 5, 6])
In [15]: np.concatenate([np.arange(i,j) for i,j in selection_idx])                                                         
Out[15]: array([0, 1, 2, 5, 6])

我没有看到 pandas 构造提供任何性能优势的任何证据。显示看起来就像输入元组中经过轻微处理的属性。

===

这是一种构造掩码的方法,无需对迭代进行循环。对于这个小案例,它可能比我的 concatenate 慢,但对于许多元组,它可能会更快:

In [42]: idx=np.array(selection_idx)                                                                                       
In [43]: idx                                                                                                               
Out[43]: 
array([[0, 3],
       [5, 7]])
In [44]: l0=idx[:,[0]]<=np.arange(7)                                                                                       
In [45]: l1=idx[:,[1]]>np.arange(7)                                                                                        
In [46]: l0 & l1                                                                                                           
Out[46]: 
array([[ True,  True,  True, False, False, False, False],
       [False, False, False, False, False,  True,  True]])
In [47]: np.any(l0&l1, axis=0)                                                                                             
Out[47]: array([ True,  True,  True, False, False,  True,  True])

受其他答案的启发,仅使用 1 个 for 循环来创建布尔掩码的列表理解:

selection_arr = np.array(selection_idx)  # convert tuples to numpy array
mask = np.full(len(arr), False)  # initialize a Boolean numpy array set to False
for b, e in selection_arr:
    mask[b:e] = True
mask
# Out: array([ True,  True,  True, False, False,  True,  True])

arr[mask]
# Out: array([0.3, 0.4, 0.5, 0.7])