Список кортежей (начало, конец) в индексном диапазоне (преобразовать массив pandas .IntervalArray в массив numpy?) - PullRequest
1 голос
/ 03 марта 2020

Цель

У меня есть массив значений и список кортежей, представляющих, какие индексы необходимо выбрать из этого массива. (Подумайте о кортежах, представляющих, какая часть звукового массива является речью.) Я думал об использовании маски выбора:

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 для маски (ей)

* Само значение 1034 * ограничено numpy.arange (из того, что я мог найти) для генерации регулярных интервальных последовательностей. Однако Pandas имеет объект pandas.IntervalArray, который можно создать с помощью таких полезных функций, как .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]

Вопросы

  • Поскольку мой вариант использования находится за пределами домена Pandas, я хотел бы знать, возможно ли преобразовать это IntervalArray объект в массиве numpy (в результате mask в соответствии с Целью)?
  • Если Pandas IntervalArray не может использоваться для моего варианта использования, какой другой подход будет? (В моем случае список неправильных кортежей составляет более 1000 на массив (с> 10.000 массивов), поэтому я ищу более эффективный подход, чем зацикливание и numpy.append)

Ответы [ 4 ]

2 голосов
/ 03 марта 2020

Одной из идей является использование списка с выравниванием:

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]
1 голос
/ 03 марта 2020

Достаточно просто присоединиться к соответствующим 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 обеспечивает какие-либо преимущества в производительности. Отображение выглядит просто как слегка обработанные атрибуты из входных кортежей.

===

Вот способ построения маски без al oop по итерациям. Для этого небольшого случая это, вероятно, медленнее, чем мой 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])
0 голосов
/ 04 марта 2020

Вдохновленный другими ответами, составьте список только с 1 для l oop, чтобы создать логическую маску:

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])
0 голосов
/ 03 марта 2020

Это может решить вашу проблему, хотя я полагаю, что в 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 s compress для получения выходных данных:

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