Подмножество панд данных, основанное на дате и времени, в другом - PullRequest
1 голос
/ 14 июня 2019

Мне нужно установить подкадр данных (df1), в котором измерения (темп) записываются каждые 5 минут, с указанием даты и времени в качестве индекса.

Фрейм данных df2 содержит данные о том, когда произошло событие.0 - начало события, а 1 - конец события.У df2 есть столбец с названием date, который является датой времени начала и конца соответствующего события.Начало и конец всех событий записываются с точностью до секунды.

Я хочу установить поднабор df1 на основе времени, когда произошло событие, используя тот же формат даты и времени, который содержится в df1 (temp для каждых 5минут).

В приведенном ниже примере произошло событие между 00:07:00 и 00:14:00, поэтому я хотел бы, чтобы df3 содержал df1 ['temp'] 00:05:00и 00:10:00Также было событие между 00:41:00 и 00:44:00, поэтому я также хотел бы, чтобы df3 содержал 00: 40: 00.

import numpy as np
import pandas as pd

df1 = pd.DataFrame({'temp' : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]},
                    index=pd.date_range('2019-05-02T00:00:00', '2019-05-02T01:00:00', freq='5T'))

df2 = pd.DataFrame({'event' : [0, 1, 0, 1],
                    'date' : ['2019-05-02-00:07:00', '2019-05-02-00:14:00', '2019-05-02-00:41:00', '2019-05-02-00:44:00']})

df2['date'] = pd.to_datetime(df2['date'])

df3 = pd.DataFrame({'result' : [2, 3, 9],
                    'date' :['2019-05-02-00:05:00', '2019-05-02-00:10:00', '2019-05-02-00:40:00']})

В моей реальной работе у меня есть 7отдельные df, каждый из которых содержит разные события, которые я хочу поместить в df1 и объединить, поэтому я получаю один df, который является подмножеством всех данных в df1, когда в любом из 7 других df было событие.На самом деле в df1 есть 37 столбцов с данными, которые я хочу перенести в окончательный вариант df3.Получив код поднабора, как описано выше, я собирался объединить все данные поднабора и удалить все дубликаты.

Ответы [ 2 ]

0 голосов
/ 14 июня 2019

Продолжая приведенный вами минимальный пример:

# create from df2 a data frame with a 'from' and 'to' column (range data frame)
def df2_like_to_from_to(df2, date_col = 'date'):
    """
    Takes 'event' column, collects all '0' and all '1' event rows
    and concatenates the columns 'date' to a data frame.
    It preserves all other columns from the first ('o') data frame.
    (That is why the code is a little more complicated).
    And renames the date_col column to 'from' and 'to' and puts them upfront.
    """
    df_from = df2[df2.event == 0]
    df_to   = df2[df2.event == 1]
    col_names = [ x if x != date_col else 'from' for x in df2_like.columns]
    df_from_to = pd.concat([df_from.reset_index(), df_to.loc[:, 'date'].reset_index()], axis=1)
    df_from_to = df_from_to.drop(columns=['index'])
    df_from_to.columns = col_names + ['to']
    df_res = df_from_to.loc[:, ['from', 'to'] + [x for x in col_names if x != 'from']]
    return df_res

range_df = df2_like_to_from_to(df2)

# 
#                  from                  to  event
# 0 2019-05-02 00:07:00 2019-05-02 00:14:00      0
# 1 2019-05-02 00:41:00 2019-05-02 00:44:00      0
# 

# filter df1 by its dates overlapping with the range in the range data frame
def filter_by_overlap(dates, df, df_from_to, from_to_col=['from', 'to']):
    """
    Filters df rows by overlaps of given dates (one per row) with da data frame
    which contains ranges (column names to be given by 'from_to_col' - first for 'from' and second for 'to' values).
    The dates are used to build a pseudo-interval which then is searched for
    any overlap with the ranges in the ranges data frame.
    The df is subsetted for any overlap and returned.
    """
    ranges_from_to = df_from_to.loc[:, from_to_col].apply(lambda x: pd.Interval(*x),  axis=1)
    ranges_date = [pd.Interval(x, x) for x in dates] # pseudo range for data points
    selector = [any(x.overlaps(y) for y in ranges_from_to) for x in ranges_date]
    return df.loc[selector, :]

filter_by_overlap(df1.index, df1, range_df)
# first argument: the data list/column for which overlaps should be searched
# second argument: the to-be-filtered data frame
# third argument: the range data frame which should select the dates (first argument)

# output:
#                      temp
# 2019-05-02 00:10:00     3
0 голосов
/ 14 июня 2019

Вы можете сделать это, используя resample и concat .
Поскольку у вас есть события, которые могут порождаться дольше, чем два лотка, вам также понадобится настраиваемая функция повторной выборки (я обнаружил, чтонет лучшего способа сделать это лучше).

event_on = 0

def event_tracker(x):
    global event_on
    if len(x) > 0:
        event_on += x.sum()
        return 1
    else:
        if event_on > 0:
            return 1
        else:
            return 0

idf2 = df2.set_index('date')
idf2['event'].loc[idf2['event'] == 0] = -1
rbdf2 = idf2.resample('5T').apply(event_tracker)
concatenated = pd.concat([df1, rbdf2], axis=1)
df3 = concatenated.loc[concatenated['event'] > 0.0]
df3 = df3.drop('event', axis=1)

Используя ваш примерный фрейм данных, вы получите df3:

                     temp
2019-05-02 00:05:00     2
2019-05-02 00:10:00     3
2019-05-02 00:40:00     9

Здесь даты устанавливаются как индексы, если по какой-то причине вам нужночтобы они были в виде столбца, добавьте заключительную строку df3 = df3.reset_index().

Позвольте мне шаг за шагом объяснить, что я сделал выше:

  • Сначала я определю пользовательскую функцию event_trackerдля пересэмплера.Это немного грязно, потому что в нем используется глобальная переменная, но я нашел самый быстрый способ сделать это.По сути, глобальная переменная используется для отслеживания происходящего события.Возвращает 0, если в ячейке нет событий, в противном случае - 1.

Затем я могу переходить строка за строкой:

  1. Установка столбца 'date' в качестве индекса.
  2. Установка 0 в idf2 (начало события) на -1.Нужно правильно выполнить математику в event_tracker.
  3. с использованием resampe.Эта функция повторно формирует кадр данных с DatetimeIndex.Я использовал пересчет 5 минут ('5T'), чтобы сопоставить ячейки в df1 (напечатайте rbdf2, чтобы увидеть это, и вы поймете)..apply() используется для применения event_tracker к каждому бину и получения 0 или 1. Как объяснено ранее.
  4. Использование concat для объединения двух фреймов данных.
  5. Выбор только тех строк, гдеevent равно> 0, это строки, в которых происходит событие.
  6. Удаление столбца 'event'.

Этот подход работает, даже если даты df2 не являютсязаказано.


Поскольку у вас есть 7 df2 s, вам нужно объединить их до , используя описанную выше процедуру.Просто сделайте:

df2 = pd.concat([df21, df22])

, где df21 и df22 - это два кадра данных с одинаковой структурой вашего df2.Если у вас есть семь фреймов данных, список, переданный concat, должен содержать все фреймы семерок: [df21, df22, df23, ...].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...