Как создать/заполнить отсутствующие строки в DataFrame?

У меня есть файл данных, который содержит потребление энергии в секунду на поезд, но приложение, создавшее файл данных, удалило все строки с 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. Сгруппируйте по поездам, чтобы получить минимальные и максимальные секунды на поезд.
  2. Создайте новый фрейм данных из продукта поезда и упорядочите (минимум, максимум) для каждого поезда. Это даст мне DataFrame с каждым поездом и каждой секундой на поезд, и без столбца энергии.
  3. Выполните левое слияние New DataFrome исходного фрейма данных, который будет иметь значения NA для энергии для любой строки, которой не было раньше.
  4. Замените все значения энергии АН на 0, и все готово.

Шаг 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.

Марк Баттен-Кэрью

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

Ответы 1

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

Вы можете использовать переиндексацию, взяв 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()

Это сработало отлично, спасибо! На всякий случай, если любой будущий читатель столкнется с моей следующей проблемой... К сожалению, первый запуск не удался, потому что оказалось, что мои данные (помимо пропущенных нескольких секунд) имеют несколько строк с повторяющимися значениями секунд, и команда переиндексации завершается ошибкой, если есть дублированный ключ. Итак, следующее, что мне нужно было сделать, это разрешить дубликаты, и тогда этот код работал нормально.

MarkBC 10.12.2020 20:00

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