Как добавить строку со значениями по умолчанию, если не в последовательном порядке

#python #python-3.x #pandas #dataframe

#python #python-3.x #панды #фрейм данных

Вопрос:

У меня есть df, подобный этому:

 time  units   cost
0      4       10
1      2       10
3      4       20
4      1       20
5      3       10
6      1       20
9      2       10
  

Как вы можете видеть, df.time не является последовательным. Если отсутствует значение, я хочу добавить новую строку, df.time заполнив последовательным значением времени, df.units с 2 помощью и df.cost с 20 помощью .
Ожидаемый результат:

 time  units   cost
0      4       10
1      2       10
2      2       20
3      4       20
4      1       20
5      3       10
6      1       20
7      2       20
8      2       20
9      2       10
  

Как мне это сделать? Я понимаю, как это сделать, разбирая все ряды на списки, просматривая их и добавляя значения, когда время не равно времени — 1, но это кажется неэффективным.

Ответ №1:

Для этого можно использовать reindex метод с вызовом fillna :

 # Build new index that ranges from time min to time max with a step of 1
new_index = range(df["time"].min(), df["time"].max()   1)


out = (df.set_index("time")                # Index our dataframe with the original time column
         .reindex(new_index)               # Reindex our dataframe with the new_index, all empty cells appear as nan
         .fillna({"units": 2, "cost": 20}) # Fill in the nans for units and cost with 2 and 20 respectively
         .astype(int))                     # Due to NaNs that were in column from reindexing, we'll manually recast our
                                           #   data type from float to int (not necessary, but produces cleaner output)

print(out)
      units  cost
time             
0         4    10
1         2    10
2         2    20
3         4    20
4         1    20
5         3    10
6         1    20
7         2    20
8         2    20
9         2    10
  

Комментарии:

1. fillna take dict забыл об этом. 1

Ответ №2:

Тогда вы можете использовать df.reindex pd.Series.fillna .

 idx = pd.RangeIndex(df['time'].min(), df['time'].max() 1) 
# If `df.time` is always sorted then,
# idx = pd.RangeIndex(df['time'].iat[0], df['time'].iat[-1] 1)

df = df.set_index('time')
df = df.reindex(idx)
df['units'] = df['units'].fillna(2).astype(int)
df['cost'] = df['cost'].fillna(20).astype(int)

# if you prefer not to hard-code the names of the columns, replace last
# the two lines with:
#   defaults = [2,20]
#   for (name, default) in zip(df.columns, defaults):
#       df[name] = df[name].fillna(default).astype(type(default))

      units  cost
time             
0         4    10
1         2    10
2         2    20
3         4    20
4         1    20
5         3    10
6         1    20
7         2    20
8         2    20
9         2    10
  

Комментарии:

1. Собираюсь отредактировать это с предложением в качестве комментария — не стесняйтесь редактировать дальше, чтобы либо включить это в фактический код, либо отменить мою правку, как вы считаете нужным…

Ответ №3:

Вы можете создать новый фрейм данных с полным столбцом «time», а затем выполнить .fillna() из исходного фрейма данных ( df это ваш исходный фрейм данных):

 r = range(df['time'].min(), df['time'].max() 1)
df_out = pd.DataFrame({'time': r, 'units': [np.nan]*len(r), 'cost': [np.nan]*len(r)}).set_index('time')

df_out = df_out.fillna(df.set_index('time'))
df_out['units'] = df_out['units'].fillna(2).astype(int)
df_out['cost'] = df_out['cost'].fillna(20).astype(int)

print(df_out)
  

С принтами:

       units  cost
time             
0         4    10
1         2    10
2         2    20
3         4    20
4         1    20
5         3    10
6         1    20
7         2    20
8         2    20
9         2    10