Фильтровать события DataFrame не во временных окнах DataFrame

У меня есть DataFrame событий (Имя события - Время) и DataFrame временных окон (Время начала - Время окончания). Я хочу получить DataFrame, содержащий только события, не относящиеся ни к одному из временных окон. Я ищу «питонический» способ фильтрации DataFrame.

Пример: Кадр данных событий:

Название события Время события Событие1 01.02.2000 00:00:00 Событие2 01.05.2000 10:00:00 Событие3 01.07.2000 09:00:00 Событие4 01.10.2000 02:00:00

Время Windows DataFrame:

Имя временного окна Время начала Время окончания Окно1 01.01.2000 00:00:00 01.06.2000 00:00:00 Окно2 01.10.2000 01:00:00 01.10.2000 04:00:00

Результат: Кадр данных отфильтрованных событий:

Название события Время события Событие3 01.07.2000 09:00:00

Настраивать:

import pandas as pd

events_data = {
    'Event Name': ['Event1', 'Event2', 'Event3', 'Event4'],
    'Event Time': ['02/01/2000 00:00:00', '05/01/2000 10:00:00', '07/01/2000 09:00:00', '10/01/2000 02:00:00']
}

time_windows_data = {
    'Time Window Name': ['Window1', 'Window2'],
    'Start Time': ['01/01/2000 00:00:00', '10/01/2000 01:00:00'],
    'End Time': ['06/01/2000 00:00:00', '10/01/2000 04:00:00']
}

events_df = pd.DataFrame(events_data)
time_windows_df = pd.DataFrame(time_windows_data)

events_df['Event Time'] = pd.to_datetime(events_df['Event Time'], format='%d/%m/%Y %H:%M:%S')
time_windows_df['Start Time'] = pd.to_datetime(time_windows_df['Start Time'], format='%d/%m/%Y %H:%M:%S')
time_windows_df['End Time'] = pd.to_datetime(time_windows_df['End Time'], format='%d/%m/%Y %H:%M:%S')
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
92
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете использовать этот подход:

inside_events_df = events_df.merge(time_windows_df, how = "cross")
inside_events_df[["Event Time", "Start Time", "End Time"]] = inside_events_df[
    ["Event Time", "Start Time", "End Time"]
].apply(pd.to_datetime)
inside_events_df = inside_events_df[
    (inside_events_df["Event Time"] > inside_events_df["Start Time"])
    & (inside_events_df["Event Time"] < inside_events_df["End Time"])
]

events_df = events_df[~events_df["Event Name"].isin(inside_events_df["Event Name"])]
  Event Name           Event Time
2     Event3  07/01/2000 09:00:00

Мне нравится этот подход, но он использует много памяти (с большими кадрами данных) из-за перекрестной операции.

Yakir Shlezinger 17.06.2024 20:36

Возможно, вы можете выполнить слияние, но я не знаю, есть ли у вас большие фреймы данных. Другой подход был бы примерно таким, хотя я не знаю, считаете ли вы его питоническим.

Настраивать:

import pandas as pd

events_data = {
    'Event Name': ['Event1', 'Event2', 'Event3', 'Event4'],
    'Event Time': ['02/01/2000 00:00:00', '05/01/2000 10:00:00', '07/01/2000 09:00:00', '10/01/2000 02:00:00']
}

time_windows_data = {
    'Time Window Name': ['Window1', 'Window2'],
    'Start Time': ['01/01/2000 00:00:00', '10/01/2000 01:00:00'],
    'End Time': ['06/01/2000 00:00:00', '10/01/2000 04:00:00']
}

events_df = pd.DataFrame(events_data)
time_windows_df = pd.DataFrame(time_windows_data)

events_df['Event Time'] = pd.to_datetime(events_df['Event Time'], format='%d/%m/%Y %H:%M:%S')
time_windows_df['Start Time'] = pd.to_datetime(time_windows_df['Start Time'], format='%d/%m/%Y %H:%M:%S')
time_windows_df['End Time'] = pd.to_datetime(time_windows_df['End Time'], format='%d/%m/%Y %H:%M:%S')

Подход 1:

# function to check if an event is within any time window
def is_event_outside_windows(event_time, windows_df):
    for _, row in windows_df.iterrows():
        if row['Start Time'] <= event_time <= row['End Time']:
            return False
    return True

filtered_events_df = events_df[events_df['Event Time'].apply(lambda x: is_event_outside_windows(x, time_windows_df))]

filtered_events_df.reset_index(drop=True, inplace=True)

print(filtered_events_df)

Подход 2:

# init mask to True for all events
mask = pd.Series([True] * len(events_df))

# update mask for each time window
for _, window in time_windows_df.iterrows():
    mask &= ~events_df['Event Time'].between(window['Start Time'], window['End Time'])

filtered_events_df = events_df[mask]

filtered_events_df.reset_index(drop=True, inplace=True)

print(filtered_events_df)

Выход:

    Event Name     Event Time
0   Event3         2000-01-07 09:00:00
Ответ принят как подходящий

Вы можете создать IntervalIndex , а затем создать логическую маску с помощью переиндекса:

# build IntervalIndex
idx = pd.IntervalIndex.from_arrays(df_time['Start Time'], df_time['End Time'])

# build boolean mask
m = (pd.Series(False, index=idx)
       .reindex(df_events['Event Time'],fill_value=True)
       .to_numpy()
    )

# select non-matching rows
out = df_events[m]

Альтернатива постройке m:

m = idx.reindex(df_events['Event Time'])[1] == -1

Выход:

  Event Name          Event Time
2     Event3 2000-01-07 09:00:00

Промежуточные продукты:

# idx
IntervalIndex([(2000-01-01 00:00:00, 2000-01-06 00:00:00],
               (2000-01-10 01:00:00, 2000-01-10 04:00:00]],
              dtype='interval[datetime64[ns], right]')

# m
array([False, False,  True, False])

Воспроизводимые входы:

import pandas as pd
from pandas import Timestamp

df_events = pd.DataFrame({'Event Name': ['Event1', 'Event2', 'Event3', 'Event4'],
                          'Event Time': [Timestamp('2000-01-02 00:00:00'),
                                         Timestamp('2000-01-05 10:00:00'),
                                         Timestamp('2000-01-07 09:00:00'),
                                         Timestamp('2000-01-10 02:00:00')]})
df_time = pd.DataFrame({'Time Window Name': ['Window1', 'Window2'],
                        'Start Time': [Timestamp('2000-01-01 00:00:00'), Timestamp('2000-01-10 01:00:00')],
                        'End Time': [Timestamp('2000-01-06 00:00:00'), Timestamp('2000-01-10 04:00:00')]})

Я получаю нехешируемый тип: 'numpy.ndarray' на out = event_data[m]

Yakir Shlezinger 17.06.2024 20:26

@YakirShlezinger, можете ли вы предоставить свои материалы в воспроизводимом формате?

mozway 17.06.2024 20:29

@Yakir проверьте обновление на предмет воспроизводимых входных данных. Также, если это не работает для вас, укажите свою версию pandas.

mozway 17.06.2024 20:34

Я все равно отредактировал пост, попробовал еще раз и вроде работает (может я что-то неправильно написал в первый раз)

Yakir Shlezinger 17.06.2024 20:34

Это действительно креативное решение!

Yakir Shlezinger 17.06.2024 20:47

Вы можете получить краткое решение с помощью piso

import piso
piso.register_accessors()

result = (
    pd.IntervalIndex.from_arrays(
        time_windows_df['Start Time'],
        time_windows_df['End Time'],
    )
    .piso.complement()
    .piso.contains(events_df.set_index('Event Name')['Event Time'], result = "points")
    .pipe(lambda x: x[x]).index
)

Отказ от ответственности: я являюсь автором Piso

Другие вопросы по теме