У меня есть файл данных, который содержит потребление энергии в секунду на поезд, но приложение, создавшее файл данных, удалило все строки с 0 для использования энергии, и мне нужно воссоздать эти строки.
В частности, необходимо следующее: для каждого номера поезда убедитесь, что есть хотя бы одна запись в секунду, используя 0 для энергии, если запись должна быть добавлена.
Мой первоначальный DataFrame выглядит так (секунды — это метка времени в секундах с полуночи):
train seconds energy
0 1024 13980 105.0000
1 1024 14745 114.0000
2 1024 14746 127.0100
3 1024 14747 137.5667
... ... ... ...
4284449 7564 95495 -301.6824
4284450 7564 95496 -181.0630
4284451 7564 95497 -60.3713
Обратите внимание, что между строкой 0 и строкой 1 существует временной разрыв 14745-13980 = 765 секунд. Насколько нам известно, единственные пробелы в посекундных записях для каждого поезда находятся между первой и второй записями, и вы можете сказать, сколько пропущено по разнице значений секунд. Но поскольку мне нужно иметь одну строку для каждой пропущенной секунды для каждого поезда, лучше не предполагать, что единственные пропущенные значения находятся между первой и второй записями.
Мой план:
Шаг 1:
# Get the minimum and maximum seconds value per train
df = df_datafile.groupby(['train'])['seconds'].agg(['min', 'max']).rename(
columns = {'min': 'minsec', 'max': 'maxsec'})
что приводит к:
minsec maxsec
train
1001 21923 25302
1003 22825 26197
1005 23736 27207
1007 24620 28009
1011 25548 28889
... ... ...
VIAE858 52785 53380
VIAE87 53442 54262
VIAE88 83204 85785
VIAE97 21942 27054
VIAE98 71123 73186
Шаг 2:
# Create one (train, second) record for every second of every train
df = DataFrame([product(*[[train], arange(minsec, maxsec)])
for train, minsec, maxsec in list(zip(df.index, df.minsec, df.maxsec))])
что приводит к:
0 1 2 ... 35403 35404 35405
0 (1001, 21923) (1001, 21924) (1001, 21925) ... None None None
1 (1003, 22825) (1003, 22826) (1003, 22827) ... None None None
2 (1005, 23736) (1005, 23737) (1005, 23738) ... None None None
3 (1007, 24620) (1007, 24621) (1007, 24622) ... None None None
4 (1011, 25548) (1011, 25549) (1011, 25550) ... None None None
... ... ... ... ... ... ... ...
2561 (VIAE858, 52785) (VIAE858, 52786) (VIAE858, 52787) ... None None None
2562 (VIAE87, 53442) (VIAE87, 53443) (VIAE87, 53444) ... None None None
2563 (VIAE88, 83204) (VIAE88, 83205) (VIAE88, 83206) ... None None None
2564 (VIAE97, 21942) (VIAE97, 21943) (VIAE97, 21944) ... None None None
2565 (VIAE98, 71123) (VIAE98, 71124) (VIAE98, 71125) ... None None None
[2566 rows x 35406 columns]
Все значения None связаны с тем, что самая длинная последовательность составляла 35406 секунд, а все остальные записи в фрейме данных должны соответствовать количеству столбцов в этой строке. Эти значения None необходимо исключить.
Но теперь я застрял. К чему я хочу прийти:
train seconds
0 1001 21923
1 1001 21924
2 1001 21925
... ... ...
??? VIAE98 71123
??? VIAE98 71124
??? VIAE98 71125
По сути, каждая отдельная строка была транспонирована (с расширенными отдельными списками и удаленными нулевыми элементами), и все транспонированные строки были объединены в один длинный фрейм данных из 2 столбцов.
Не могли бы вы помочь мне с этим последним шагом и/или дать мне какой-нибудь другой способ сделать исходную постановку задачи (выделено курсивом в начале).
Большое спасибо за любую помощь. Я очень ценю всех, кто отвечает на вопросы StackOverflow.
Марк Баттен-Кэрью
Вы можете использовать переиндексацию, взяв min
и max
секунд на группу поездов:
def populate_df(grp):
grp = (grp.set_index('seconds')
.reindex(range(grp.seconds.min(), grp.seconds.max()+1))
.drop(columns='train')
.fillna(0)
)
return grp
df.groupby('train').apply(populate_df).reset_index()
Это сработало отлично, спасибо! На всякий случай, если любой будущий читатель столкнется с моей следующей проблемой... К сожалению, первый запуск не удался, потому что оказалось, что мои данные (помимо пропущенных нескольких секунд) имеют несколько строк с повторяющимися значениями секунд, и команда переиндексации завершается ошибкой, если есть дублированный ключ. Итак, следующее, что мне нужно было сделать, это разрешить дубликаты, и тогда этот код работал нормально.