Как сопоставить прогнозы за несколько временных интервалов с фактическими значениями?

У меня есть скрипт Python, который выдает прогнозы для временной серии за несколько временных шагов, например:

Я хотел бы повернуть или изменить порядок прогнозов, чтобы у меня был новый столбец с именем Predictions. В этом столбце у меня есть последовательный порядок Prediction_1, Prediction_2 и т. д. А затем у нас есть четыре столбца time_from_actual_1, time_from_actual и т. д., которые содержат значения прогноза. Таким образом, для каждой строки у нас есть дата, фактическое значение, значение «Прогнозы», которое указывает количество прогнозов, а затем четыре значения, которые показывают четыре разных временных шага в прогнозах.

Вывод должен выглядеть так:

Вот что я пробовал с примерами данных:


import pandas as pd
import numpy as np

# Set random seed for reproducibility
np.random.seed(42)

# Create a DataFrame with daily values for 10 days
date_range = pd.date_range(start = "2023-01-01", end = "2023-01-10", freq='D')
actual_values = np.random.randint(90, 110, size=len(date_range))

df = pd.DataFrame({'Date': date_range, 'Actual': actual_values})

# Create prediction columns
prediction_period = 4  # 4 days for each prediction
predictions_df = df.copy()

for i in range(len(df)):
    prediction_dates = pd.date_range(start=df['Date'][i], periods=prediction_period, freq='D')
    predictions = np.random.randint(90, 110, size=prediction_period)

    # Add the predictions to the DataFrame
    for j, pred_date in enumerate(prediction_dates):
        pred_col_name = f'prediction_{i+1}'
        if pred_date in predictions_df['Date'].values:
            predictions_df.loc[predictions_df['Date'] == pred_date, pred_col_name] = predictions[j]

# Fill NaN values with empty strings
predictions_df = predictions_df.fillna('')

# Reshape the DataFrame
reshaped_data = []

for i in range(len(df)):
    prediction_name = f'prediction_{i+1}'
    if prediction_name in predictions_df.columns:
        for j in range(prediction_period):
            if i + j < len(predictions_df):
                reshaped_data.append({
                    'Date': predictions_df['Date'][i + j],
                    'Actual': predictions_df['Actual'][i + j],
                    'Prediction': prediction_name,
                    'time_from_actual_1': predictions_df.loc[i + j, prediction_name] if j == 0 else '',
                    'time_from_actual_2': predictions_df.loc[i + j, prediction_name] if j == 1 else '',
                    'time_from_actual_3': predictions_df.loc[i + j, prediction_name] if j == 2 else '',
                    'time_from_actual_4': predictions_df.loc[i + j, prediction_name] if j == 3 else '',
                })

reshaped_df = pd.DataFrame(reshaped_data)


Спасибо за любые предложения!

Можете ли вы иметь NaN между некоторыми числами? Если да, что должно произойти?

mozway 18.07.2024 15:45

@mozway NaN не будет проблемой в моем реальном наборе данных. Хотя это был бы интересный вызов. NaN также будут отображаться как NaN на соответствующем временном шаге в зависимости от разницы между фактической датой и прогнозируемым значением.

vestland 18.07.2024 20:45
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
60
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

cols = ['Date', 'Actual']

out = predictions_df[cols].join(predictions_df
 .filter(like='prediction_')
 .melt(ignore_index=False, var_name='Predictions')
 .replace({'value': {'': None}})
 .dropna(subset='value')
 .assign(idx=lambda x: x.groupby('Predictions')['Predictions'].transform('idxmin'),
         col=lambda x: x.groupby('idx').cumcount().add(1)
        )
 .pivot(index=['idx', 'Predictions'], columns='col', values='value')
 .reset_index('Predictions').add_prefix('time_from_actual_')
)

Другой подход заключается в смещении исходных непустых данных вверх по каждому столбцу и транспонировании. Это зависит от индекса диапазона в исходном наборе данных.

cols = ['Date', 'Actual']

out = predictions_df[cols].join(predictions_df
 .filter(like='prediction_')
 .apply(lambda x: x[x.ne('').cummax() & x[::-1].ne('').cummax()]
                   .reset_index(drop=True))
 .rename(lambda x: f'time_from_actual_{x+1}')
 .T.rename_axis('Predictions').reset_index()
)

Выход:

        Date  Actual time_from_actual_Predictions time_from_actual_1 time_from_actual_2 time_from_actual_3 time_from_actual_4
0 2023-01-01      96                 prediction_1               97.0               92.0               91.0              101.0
1 2023-01-02     109                 prediction_2               95.0               91.0               90.0              101.0
2 2023-01-03     104                 prediction_3              101.0              106.0               99.0              105.0
3 2023-01-04     100                 prediction_4              104.0              104.0              108.0              101.0
4 2023-01-05      97                 prediction_5              109.0               92.0               94.0              108.0
5 2023-01-06      96                 prediction_6               96.0               98.0               96.0              107.0
6 2023-01-07     108                 prediction_7               93.0              103.0              107.0               98.0
7 2023-01-08     100                 prediction_8               91.0              109.0              104.0                NaN
8 2023-01-09     100                 prediction_9              101.0               97.0                NaN                NaN
9 2023-01-10      93                prediction_10              103.0                NaN                NaN                NaN

используйте функцию df.pivot после заполнения значений NaN пустыми строками

predictions_df = predictions_df.fillna('')

Одним из вариантов было бы сбросить данные в numpy, воспользоваться np.diagonal, транспонировать и соединить соответствующие части, чтобы сформировать новый фрейм данных. Обратите внимание, что этот ответ относится только к общему фрейму данных; если вы можете изменить высоту и столбцы фрейма данных, я мог бы адаптировать его, чтобы он был более общим:

import pandas as pd
import numpy as np

predictions_only=predictions_df.filter(like='prediction')

columns=predictions_only.columns

numpy_array = (predictions_only
               # you can ignore this conversion
               # i did not want to lose the perf.
               # benefits of working with numbers in numpy
               .astype(str)
               .replace('',np.nan)
               .astype(float)
               .to_numpy()
               )

diagonals = [numpy_array.diagonal(-1*num) for num in range(4)]

max_length = max(map(len, diagonals))

new_arr=np.full(shape, fill_value=np.nan)

for num, array in enumerate(diagonals):
    new_arr[num,:array.size] = array

reshaped=pd.DataFrame(new_arr.T,columns=new_arr_columns)

new_arr_columns = [f'time_from_actual_{num}' for num in range(1,5)]

to_concat = [predictions_df.Date, predictions_df.Actual, 
             pd.Series(columns,name='Predictions'), reshaped]

pd.concat(to_concat,axis=1)

        Date  Actual    Predictions  time_from_actual_1  time_from_actual_2  time_from_actual_3  time_from_actual_4
0 2023-01-01      96   prediction_1                97.0                92.0                91.0               101.0
1 2023-01-02     109   prediction_2                95.0                91.0                90.0               101.0
2 2023-01-03     104   prediction_3               101.0               106.0                99.0               105.0
3 2023-01-04     100   prediction_4               104.0               104.0               108.0               101.0
4 2023-01-05      97   prediction_5               109.0                92.0                94.0               108.0
5 2023-01-06      96   prediction_6                96.0                98.0                96.0               107.0
6 2023-01-07     108   prediction_7                93.0               103.0               107.0                98.0
7 2023-01-08     100   prediction_8                91.0               109.0               104.0                 NaN
8 2023-01-09     100   prediction_9               101.0                97.0                 NaN                 NaN
9 2023-01-10      93  prediction_10               103.0                 NaN                 NaN                 NaN

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

Суммирование перестановок в фрейме данных Pandas растет суперэкспоненциально
Найдите значение в столбце, который содержит список, возьмите другое значение из следующего столбца и поместите его в первую таблицу в новый столбец
Python pandas read_sas с параметром размера фрагмента завершается с ошибкой из-за несоответствия индекса
Как я могу заполнить значение на основе другого категориального столбца
Что означает: приведение данных Pandas к numpy dtype объекта. Проверьте входные данные с помощью np.asarray(data) и как это можно решить?
Как обобщить фрейм данных в пандах на основе значений
Отсутствует модуль Sklearn и вы не знаете, что использовать вместо него?
Как я могу эффективно фильтровать и агрегировать данные в Pandas DataFrame с несколькими условиями?
Существует ли идиома Pandas для чтения файла CSV с категориальными данными, имеющими варианты написания?
Разделить столбец данных pandas на несколько на основе текстовых значений