Numpy: как извлечь строки массива numpy между несколькими парами значений? - PullRequest
1 голос
/ 28 мая 2020

Я относительно новичок в Python, и в настоящее время я сталкиваюсь с некоторыми проблемами при реализации концептуально простого алгоритма эффективным способом. Мне удалось сделать это в pandas (но он выполняется довольно медленно).

У меня есть ndarray, состоящий из n строк и 3 столбцов:

--------------------
  "A"  |  1  |  12
--------------------
  "B"  |  2  |  34
--------------------
  "S"  |  3  |  1
--------------------
  "B"  |  4  |  145
--------------------
  "A"  |  5  |  132
--------------------
  "B"  |  6  |  234
--------------------
  "E"  |  7  |  1
--------------------
  "B"  |  8  |  15
--------------------

Первый столбец представляет id , второй столбец - метку времени , а третий - значение . Мне нужно отфильтровать ndarray, беря только строки с отметками времени, включенными между отметкой времени id "S" (начало) и отметкой времени id "E" (конец). Возможно иметь более одной пары «S» и «E» в одном ndarray. В случае непоследовательной пары «S» и «E» мне нужен самый короткий подмассив. Другими словами, в выводе не должно появляться идентификаторов «S» или «E».

Таким образом, результат должен быть:

--------------------
  "B"  |  4  |  145
--------------------
  "A"  |  5  |  132
--------------------
  "B"  |  6  |  234
--------------------

Как уже было сказано, я получил этот результат, используя pandas, но функция очень длинная, сложная и выполняется очень медленно. Поэтому я уверен, что с помощью numpy можно получить лучший и наиболее эффективный алгоритм.

Есть ли у вас какие-нибудь идеи?

Заранее спасибо.

РЕДАКТИРОВАТЬ

Вот фрагмент кода, который дает ожидаемые результаты с использованием pandas. Время выполнения составляет около 0,015 секунды на процессоре Intel Core i7-6820 @ 2,70 ГГц.

df = pd.DataFrame({'id': ['A', 'B', 'C', 'S', 'C', 'C', 'A', 'B', 'E', 'A', 'C', 'B', 'B', 'S', 'C', 'A', 'E', 'B'],
                   't': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
                   'v': [145, 543, 12, 1, 14, 553, 65, 657, 1, 32, 54, 22, 11, 1, 6, 22, 1, 4]})
print(df)
res = pd.DataFrame()
id = "id"
t = "t"
v = "v"
id_bit_start = "S"
id_bit_end = "E"

# taking only "S" and "E" from df (when their value is 1)
df_data_bit = df.loc[
    ((df[id] == id_bit_start) |
     (df[id] == id_bit_end)) &
    (df[v] == 1.0)
    ]

# do something only if at least one "S" is present
if id_bit_start in df_data_bit[id].values:
    # creating empty list of time windows
    t_windows = list()
    t_wind_temp = [None, None]

    # for each bit "S" or "E"
    for index, bit in df_data_bit.iterrows():
        # if it is a "S"
        if bit[id] == id_bit_start:
            # set the start of the time window
            t_wind_temp[0] = bit[t]
        # if it is a "E" and the "S" has already been processed
        elif t_wind_temp[0] is not None:
            # set the end of the time window
            t_wind_temp[1] = bit[t]
            # append the current time window to our list
            t_windows.append(t_wind_temp)
            # reset the current time window
            t_wind_temp = [None, None]

    # taking everything but "S" and "E"
    df_data = df.loc[
        ~((df[id] == id_bit_start) |
          (df[id] == id_bit_end))
    ]

    # for each created time window
    for t_window in t_windows:
        # take only data with timestamps between the time window
        result = df_data.loc[
            (df_data[t] >= t_window[0])
            &
            (df_data[t] <= t_window[1])
            ]
        # append to the final result
        res = pd.concat([res, result])

print(res)

1 Ответ

0 голосов
/ 28 мая 2020

Это позаботится о вашей неопределенности последовательности S и E:
Предполагая, что ваши временные метки расположены в возрастающем порядке:

import re

a = df.to_records(index=False)
idx = [m.span() for m in re.finditer('S[^{SE}]*?E', ''.join(a['id']))]
indexer = np.r_[tuple([np.s_[i+1:j-1] for (i,j) in idx])]
a_filtered = a[indexer]

Пояснение :

Есть несколько уловок для быстрого вычисления этого:

  1. преобразовать ваш фрейм данных в структурированный массив
  2. преобразовать все символы id в строку
  3. искать не жадные совпадения of S.*?E (обратите внимание, что вы можете изменить S и E на любые подстроки, если ваш идентификатор не является одной буквой)
  4. получить индексы начала и конца подстрок, которые вы найдете
  5. создать список все индексы между этими индексами в 4
  6. фильтруют ваш массив, используя индексы в 5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...