Как я могу повернуть интервалы дат, указанные в столбцах, и извлечь объемы в час за интервал времени для нескольких перекрывающихся периодов?

У меня есть следующий df:

Start_Date   End_Date  Relevant  Volume   
2024-10-01 2024-12-31     False  0.000000   
2025-01-01 2025-03-31      True  0.097989   
2025-04-01 2025-06-30      True -0.014449   
2025-01-01 2025-12-31      True  0.195327   
2026-01-01 2026-12-31     False  0.000000   

Мне нужен почасовой индекс, начинающийся/заканчивающийся в первую/последнюю дату, где Релевантно == True. Я делаю это следующим образом:

    relevant_df = df[df['Relevant']]    
    earliest_start = relevant_df['Start_Date'].min()
    latest_end = relevant_df['End_Date'].max()
    # Create DateTime index
    date_range = pd.date_range(start=earliest_start, end=latest_end, freq='H')
    aggregated_volumes = pd.Series(index=date_range, dtype=float)

Теперь, как я могу взять объем за период и сложить их вместе, чтобы в этом примере, в первые три месяца 2025 года, объем в час был равен 0,097989 + 0,195327, второй квартал -0,014449 + 0,195327 и т. д.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
67
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Поскольку ваши интервалы перекрываются, я считаю, что нет прямого способа индексировать ваши значения.

Однако вы можете создать массив NxM numpy (N: количество строк, M: количество истинных строк) и суммировать:

# ensure datetime
relevant_df[['Start_Date', 'End_Date']] = relevant_df[['Start_Date', 'End_Date']].apply(pd.to_datetime)

# compute a mask of the values between start/end
idx = date_range.to_numpy()[:, None]
m1 = idx>=relevant_df['Start_Date'].to_numpy()
m2 = idx<relevant_df['End_Date'].to_numpy()

# broadcast the values, sum, convert to Series
out = pd.Series(np.nansum(np.where(m1&m2, relevant_df['Volume'].to_numpy(), np.nan), axis=1),
                index=date_range)

Выход:

2025-01-01 00:00:00    0.293316
2025-01-01 01:00:00    0.293316
2025-01-01 02:00:00    0.293316
2025-01-01 03:00:00    0.293316
2025-01-01 04:00:00    0.293316
                         ...   
2025-12-30 20:00:00    0.195327
2025-12-30 21:00:00    0.195327
2025-12-30 22:00:00    0.195327
2025-12-30 23:00:00    0.195327
2025-12-31 00:00:00    0.000000
Freq: h, Length: 8737, dtype: float64

В качестве альтернативы, если у вас редкие интервалы относительно всего диапазона:

out = pd.DataFrame([pd.Series(v, index=pd.date_range(s, e, inclusive='left', freq='h'))
                    for s,e,v in zip(relevant_df['Start_Date'],
                                     relevant_df['End_Date'],
                                     relevant_df['Volume']
                                    )]).sum()

Выход:

2025-01-01 00:00:00    0.293316
2025-01-01 01:00:00    0.293316
2025-01-01 02:00:00    0.293316
2025-01-01 03:00:00    0.293316
2025-01-01 04:00:00    0.293316
                         ...   
2025-12-30 19:00:00    0.195327
2025-12-30 20:00:00    0.195327
2025-12-30 21:00:00    0.195327
2025-12-30 22:00:00    0.195327
2025-12-30 23:00:00    0.195327
Freq: h, Length: 8736, dtype: float64

Визуальный вывод:

Последние 23 часа не заполняются объемами даже после изменения приведенного выше значения на:latest_end =релевантная_база['End_Date'].max() + pd.DateOffset(hours=23). Все последние 23 строки равны нулю. Есть идеи, как это исправить?

cJc 23.04.2024 09:09
import pandas as pd
import numpy as np
from scipy.sparse import coo_matrix, csc_matrix, csr_matrix


data = {
    'Start_Date': ['2024-10-01', '2025-01-01', '2025-04-01', '2025-01-01', '2026-01-01'],
    'End_Date': ['2024-12-31', '2025-03-31', '2025-06-30', '2025-12-31', '2026-12-31'],
    'Relevant': [False, True, True, True, False],
    'Volume': [0.0, 0.097989, -0.014449, 0.195327, 0.0]
}

df = pd.DataFrame(data)

# Ensure datetime format
df['Start_Date'] = pd.to_datetime(df['Start_Date'])
df['End_Date'] = pd.to_datetime(df['End_Date'])
print(df)

  Start_Date   End_Date  Relevant    Volume
0 2024-10-01 2024-12-31     False  0.000000
1 2025-01-01 2025-03-31      True  0.097989
2 2025-04-01 2025-06-30      True -0.014449
3 2025-01-01 2025-12-31      True  0.195327
4 2026-01-01 2026-12-31     False  0.000000
# Filter rows where Relevant == True
relevant_df = df[df['Relevant']]
print(relevant_df)
  Start_Date   End_Date  Relevant    Volume
1 2025-01-01 2025-03-31      True  0.097989
2 2025-04-01 2025-06-30      True -0.014449
3 2025-01-01 2025-12-31      True  0.195327
#generate hourly date range
date_range = pd.date_range(start= relevant_df['Start_Date'].min(), 
                           end =relevant_df['Start_Date'].max() ,freq = 'h')

print(date_range.to_series())
2025-01-01 00:00:00   2025-01-01 00:00:00
2025-01-01 01:00:00   2025-01-01 01:00:00
2025-01-01 02:00:00   2025-01-01 02:00:00
2025-01-01 03:00:00   2025-01-01 03:00:00
2025-01-01 04:00:00   2025-01-01 04:00:00
                              ...        
2025-03-31 20:00:00   2025-03-31 20:00:00
2025-03-31 21:00:00   2025-03-31 21:00:00
2025-03-31 22:00:00   2025-03-31 22:00:00
2025-03-31 23:00:00   2025-03-31 23:00:00
2025-04-01 00:00:00   2025-04-01 00:00:00
Freq: h, Length: 2161, dtype: datetime64[ns]
date_range_length = len(date_range)
#print(date_range_length)#2161
  • rows: этот массив определяет, к какой строке принадлежит каждый элемент данных. Он создается путем повторения индексов соответствующих записей (len(relevant_df)) date_range_length раз.

  • cols: этот массив определяет, какому столбцу принадлежит каждый элемент данных. получить значения относительно first_non_null_indices

rows = np.repeat(np.arange(len(relevant_df)), len(date_range))
#print(rows)#[0 0 0 ... 2 2 2]
cols = np.tile(np.arange(len(date_range)), len(relevant_df))
#print(cols)#[   0    1    2 ... 2158 2159 2160]

relevant_volume_data = relevant_df['Volume'].values.repeat(len(date_range))
relevant_volume_data : 
[0.097989 0.097989 0.097989 ... 0.195327 0.195327 0.195327]
  • `np.searchsorted используется для поиска точек вставки в отсортированном массиве. Он быстро определяет, где каждая Start_Date и End_Date будет соответствовать диапазону дат.
start_indices = np.searchsorted(date_range, relevant_df['Start_Date'].values)
end_indices = np.searchsorted(date_range, relevant_df['End_Date'].values)
  • Почему бы и нет (cols <= end_indices[rows]) вместо (cols < end_indices[rows])?

-Ans :

  • Если start_indices и end_indices имеют повторяющиеся значения условие (cols < end_indices[rows]) может исключить последний элемент этой записи по сравнению с (cols <= end_indices[rows])
mask = (cols >= start_indices[rows]) & (cols < end_indices[rows])
rows_masked,cols_masked,relevant_volume_data_masked = rows[mask], cols[mask], relevant_volume_data[mask]

csr_sparse_matrix = csr_matrix( (relevant_volume_data_masked, (rows_masked,cols_masked)),

shape =  (len(relevant_df), len(date_range) )
) 
  (0, 0)    0.097989
  (0, 1)    0.097989
  (0, 2)    0.097989
  (0, 14)    0.097989
  (0, 15)    0.097989
  (0, 21)    0.097989
  (0, 22)    0.097989
  (0, 23)    0.097989
  (0, 24)    0.097989
  :    :
  (2, 2136)    0.195327
  (2, 2137)    0.195327
  (2, 2138)    0.195327
  (2, 2139)    0.195327
  (2, 2140)    0.195327
  (2, 2159)    0.195327
  (2, 2160)    0.195327
summed_volumes = np.array(csr_sparse_matrix.sum(axis=0)).flatten()
summed_volumes:
[0.293316 0.293316 0.293316 ... 0.195327 0.195327 0.180878]
res = pd.Series(summed_volumes,index = date_range)
print(res)
2025-01-01 00:00:00    0.293316
2025-01-01 01:00:00    0.293316
2025-01-01 02:00:00    0.293316
2025-01-01 03:00:00    0.293316
2025-01-01 04:00:00    0.293316
                         ...   
2025-03-31 20:00:00    0.195327
2025-03-31 21:00:00    0.195327
2025-03-31 22:00:00    0.195327
2025-03-31 23:00:00    0.195327
2025-04-01 00:00:00    0.180878
Freq: h, Length: 2161, dtype: float64
  • Полный код:
import pandas as pd
import numpy as np
from scipy.sparse import coo_matrix, csc_matrix, csr_matrix


data = {
    'Start_Date': ['2024-10-01', '2025-01-01', '2025-04-01', '2025-01-01', '2026-01-01'],
    'End_Date': ['2024-12-31', '2025-03-31', '2025-06-30', '2025-12-31', '2026-12-31'],
    'Relevant': [False, True, True, True, False],
    'Volume': [0.0, 0.097989, -0.014449, 0.195327, 0.0]
}

df = pd.DataFrame(data)


df['Start_Date'] = pd.to_datetime(df['Start_Date'])
df['End_Date'] = pd.to_datetime(df['End_Date'])

relevant_df = df[df['Relevant']]

date_range = pd.date_range(start= relevant_df['Start_Date'].min(), 
                           end =relevant_df['Start_Date'].max() ,freq = 'h')
print(date_range.to_series())

date_range_length = len(date_range)


rows = np.repeat(np.arange(len(relevant_df)), len(date_range))
cols = np.tile(np.arange(len(date_range)), len(relevant_df))
relevant_volume_data =   relevant_df['Volume'].values.repeat(len(date_range))

start_indices = np.searchsorted(date_range, relevant_df['Start_Date'].values)
end_indices = np.searchsorted(date_range, relevant_df['End_Date'].values)

mask = (cols >= start_indices[rows]) & (cols < end_indices[rows])
rows_masked,cols_masked,relevant_volume_data_masked = rows[mask], cols[mask], relevant_volume_data[mask]

csr_sparse_matrix = csr_matrix( (relevant_volume_data_masked, (rows_masked,cols_masked)),
shape =  (len(relevant_df), len(date_range) )
)           
summed_volumes = np.array(csr_sparse_matrix.sum(axis=0)).flatten()
res = pd.Series(summed_volumes,index = date_range)

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