Как создать двоичный фрейм данных с DatetimeIndex, в котором указано событие с объектом date в качестве фильтров?

#python #python-3.x #pandas #dataframe

#python #python-3.x #pandas #фрейм данных

Вопрос:

Скажем, у меня есть список объектов date dates = ['2020-03-01', '2020-05-10'] и фрейм данных, индексированный с помощью объекта datetime :

введите описание изображения здесь

Набор данных был сгенерирован из другого с использованием методов передискретизации pandas с частотой «1h».

Я хотел бы заполнить значения столбцов A и B, используя список dates , содержащий объект date. Точнее, если индекс совпадает с днем элемента dates , я хочу поместить соответствующую строку и все предыдущие строки за 1 месяц с 1 в качестве значений. Остальные строки должны быть заполнены 0 в качестве значений.

Моя стратегия состоит в том, чтобы начать с нулевого фрейма данных, разделяющего столбцы и индекс исходного. Но у меня возникают некоторые проблемы, когда я хочу заполнить значения. На самом деле, я могу сделать это с помощью цикла foor, но я верю в то, что pandas достаточно мощный, чтобы выполнить эту работу с помощью нескольких строк.

Кто-нибудь может мне помочь? Я был бы признателен за любую помощь.

Редактировать: чтобы упростить кодирование / тестирование, я немного изменил формулировку проблемы. Индекс даты и времени теперь равен всем часам 2020-01-01. Приведенный ниже код, похоже, работает хорошо, но я не вижу способа перебирать список дат без цикла for … Вы можете начать с этого. Код :

 import pandas as pd
import numpy as np

datetime_index = pd.date_range(start='2020-01-01 00:00:00', 
                           end='2020-01-01 23:59:59', freq='1h')
n_cols = 2
n_rows = datetime_index.size
df_shape =(n_rows, n_cols)

dates = ['2020-01-01 05:00:00']

df = pd.DataFrame(np.random.randint(0,10, size=df_shape),
                  index = datetime_index.values,
                  columns = [f'column {i}' for i in range(n_cols)])

data = pd.DataFrame(np.zeros(df_shape).astype(int),
                  index = datetime_index.values,
                  columns = [f'column {i}' for i in range(n_cols)])

data['column 0'] = data.apply(lambda x: int(x.name in pd.date_range(
                start=pd.to_datetime(dates[0]) - pd.Timedelta(1, unit='hours'),
                end=pd.to_datetime(dates[0]),
                freq='1h'
)), axis=1)
  

Комментарии:

1. Вы должны привести пример, над которым люди могут работать.

2. Конечно, минутку, я его предоставлю.

3. @user2640045 Я добавил код для начала, и, похоже, он работает хорошо. Но я не вижу способа перебирать даты без цикла for .

Ответ №1:

Вот немного другая версия:

 import pandas as pd

window = 30 # days
targets = ['2020-03-01', '2020-05-10']
dates = pd.date_range(start='2020-01-29', end='2020-07-01', freq='14d')

# create data frame
t = pd.DataFrame(index=dates)

# is target date close to i-th date?
for target in targets:
    t[target] = abs((t.index - pd.Timestamp(target)).days) < window
    
print(t.astype(int))

            2020-03-01  2020-05-10
2020-01-29           0           0
2020-02-12           1           0
2020-02-26           1           0
2020-03-11           1           0
2020-03-25           1           0
2020-04-08           0           0
2020-04-22           0           1
2020-05-06           0           1
2020-05-20           0           1
2020-06-03           0           1
2020-06-17           0           0
2020-07-01           0           0
  

Ответ №2:

Я изменил частоту на две недели, чтобы мне было легче проверить свой результат

 import pandas as pd
import numpy as np
from datetime import datetime

datetime_index = pd.date_range(start='2020-01-01 00:00:00', 
                               end='2020-10-01 23:59:59', freq=f'{2*24*7}h')
n_cols = 2
n_rows = datetime_index.size
df_shape =(n_rows, n_cols)

dates = ['2020-03-01', '2020-05-10']

def to_datetime(x):
    year, month, day = map(int, x.split("-"))
    return datetime(year=year, month=month, day=day)
dates = list(map(to_datetime, dates))

date_series = pd.Series(datetime_index, index=datetime_index)
pd.DataFrame({f"column{i}":date_series
              .apply(lambda x:int(-pd.Timedelta(days=30)<(x-dates[i])<pd.Timedelta(days=30))) 
              for i in range(len(dates))})
  

это дает

             column0  column1
2020-01-01        0        0
2020-01-15        0        0
2020-01-29        0        0
2020-02-12        1        0
2020-02-26        1        0
2020-03-11        1        0
2020-03-25        1        0
2020-04-08        0        0
2020-04-22        0        1
2020-05-06        0        1
2020-05-20        0        1
2020-06-03        0        1
2020-06-17        0        0
2020-07-01        0        0
2020-07-15        0        0
2020-07-29        0        0
2020-08-12        0        0
2020-08-26        0        0
2020-09-09        0        0
2020-09-23        0        0
  

Комментарии:

1. Ваша работа почти выполнила свою работу. Фактически, в данной строке значения должны быть равны одинаковому и уникальному значению (либо 0, либо 1). Это заставляет меня думать, что нам не нужно много столбцов.. он просто использует память ни для чего интересного.

2. @kakarotto Вы говорите, что хотите объединить логические значения (на которых основаны числа) с amp; ? Вы заставляете меня думать, что вам следует это прочитать pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html или понаблюдайте за этим youtu.be/Pau9An-fQZk :).

3. @user2640045 Я имею в виду, что исходный фрейм данных должен быть скопирован, но его значения должны быть либо 0, либо 1. Если строка имеет индекс, соответствующий одной из дат в списке, для нее должно быть задано значение 1, а для всех предыдущих строк по 1 единице также должно быть задано значение 1 (где единица изначально равна месяцу, но это не имеет значения, поскольку это не меняет процесс). В любом случае, спасибо, что ваш ответ с ответом jsmart помог мне решить мою проблему. Спасибо, ребята.