У меня есть скрипт 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)
Спасибо за любые предложения!
@mozway NaN не будет проблемой в моем реальном наборе данных. Хотя это был бы интересный вызов. NaN также будут отображаться как NaN на соответствующем временном шаге в зависимости от разницы между фактической датой и прогнозируемым значением.
Вы можете изменить форму с помощью расплавить , избавиться от пустых строк, затем назначить начальный индекс для каждой группы и повернуть + присоединиться:
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
Можете ли вы иметь NaN между некоторыми числами? Если да, что должно произойти?