Фрейм данных Pandas заменяет NaN ближайшим минимальным значением в столбце

#python #pandas #dataframe #nan

#python #pandas #фрейм данных #nan

Вопрос:

У меня есть фрейм данных pandas со столбцом с именем ‘A_col’, и я хотел бы создать новый столбец с именем ‘A_col_fill’, который заменит NaN в ‘A_col’ на минимальное значение непосредственно перед ним, если оно есть. Пример вывода выглядит следующим образом.

             A_col           A_col_fill
0            NaN                 NaN
1            NaN                 NaN
2            NaN                 NaN
3            NaN                 NaN
4            NaN                 NaN
5            NaN                 NaN
6            NaN                 NaN
7           -0.3400             -0.3400
8            NaN                -0.3400
9            NaN                -0.3400
10          -0.1900             -0.1900
11            NaN               -0.1900
12          -0.3700             -0.3700
13          -0.4100             -0.4100
14          -0.3300             -0.3300
15            NaN               -0.4100
16            NaN               -0.4100
17            NaN               -0.4100
18            NaN               -0.4100
19            NaN               -0.4100
20          -1.6500             -1.6500
21          -1.8000             -1.8000
22          -1.5300             -1.5300
23          -1.3500             -1.3500
24            NaN               -1.8000
25          -0.1900             -0.1900
26          -0.1400             -0.1400
28          -0.2100             -0.2100
  

Похоже, что функция ‘fillna’ фрейма данных не работает с регистром, как я могу это реализовать, любой фрагмент кода высоко ценится!

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

1. Не могли бы вы добавить некоторую информацию о правилах, по которым он должен выбирать? похоже, что правило состоит в том, чтобы выбрать минимум (предыдущий непрерывный блок [не NaN]), это правильно?

2. Не должно ли значение с индексом 11 быть -0.3400? Самый простой способ реализовать это — поместить этот столбец в список, а затем выполнить итерацию по этому списку, отслеживая текущий минимум.

3. точно! Мне нравится выбирать минимум предыдущего непрерывного блока для замены NaN

4. @MachineLearner, индекс 11 равен -0.1900

Ответ №1:

Это решение будет заполнять nan с помощью следующего минимального значения последнего «острова» смежных строк, содержащих значения. Он должен быть более точным и производительным, чем другие предлагаемые решения (за счет усложнения):

  • создайте столбец с номером группы для каждого «острова» смежных значений или nan
  • получите минимальное значение для каждой группы; перенаправьте заполнение строк nan предыдущим минимальным значением
  • заполните исходный столбец новым минимальным значением для группы

код:

 df["group_col"] = np.cumsum(df["A_col"].isna() != df["A_col"].isna().shift())
df["group_min"] = df.groupby("group_col").A_col.transform(min).ffill()
df["output"] = df["A_col"].fillna(df.group_min)
  

Результат:

     A_col  A_col_fill  group_col  group_min  output
0     NaN         NaN          1        NaN     NaN
1     NaN         NaN          1        NaN     NaN
2     NaN         NaN          1        NaN     NaN
3     NaN         NaN          1        NaN     NaN
4     NaN         NaN          1        NaN     NaN
5     NaN         NaN          1        NaN     NaN
6     NaN         NaN          1        NaN     NaN
7   -0.34       -0.34          2      -0.34   -0.34
8     NaN       -0.34          3      -0.34   -0.34
9     NaN       -0.34          3      -0.34   -0.34
10  -0.19       -0.19          4      -0.19   -0.19
11    NaN       -0.19          5      -0.19   -0.19
12  -0.37       -0.37          6      -0.41   -0.37
13  -0.41       -0.41          6      -0.41   -0.41
14  -0.33       -0.33          6      -0.41   -0.33
15    NaN       -0.41          7      -0.41   -0.41
16    NaN       -0.41          7      -0.41   -0.41
17    NaN       -0.41          7      -0.41   -0.41
18    NaN       -0.41          7      -0.41   -0.41
19    NaN       -0.41          7      -0.41   -0.41
20  -1.65       -1.65          8      -1.80   -1.65
21  -1.80       -1.80          8      -1.80   -1.80
22  -1.53       -1.53          8      -1.80   -1.53
23  -1.35       -1.35          8      -1.80   -1.35
24    NaN       -1.80          9      -1.80   -1.80
25  -0.19       -0.19         10      -0.21   -0.19
26  -0.14       -0.14         10      -0.21   -0.14
28  -0.21       -0.21         10      -0.21   -0.21
  

Решение занимает миллисекунды для 1 м строки df на моей машине:

 df = pd.DataFrame(np.random.random(size=100000), columns=["A_col"])
df.loc[df.sample(frac=0.6).index, "A_col"] = np.nan
# code from above
df["group_col"] = np.cumsum(df["A_col"].isna() != df["A_col"].isna().shift())
df["group_min"] = df.groupby("group_col").A_col.transform(min).ffill()
df["output"] = df["A_col"].fillna(df.group_min)
  

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

1. Чувак, хороший, четкий ответ! это именно то, что я ищу. Спасибо

Ответ №2:

 p['A_col'].fillna(np.inf).replace(np.inf,p['A_col'].ffill().cummin())
  

вывод:

 0      NaN
1      NaN
2      NaN
3      NaN
4      NaN
5      NaN
6      NaN
7    -0.34
8    -0.34
9    -0.34
10   -0.19
11   -0.34
12   -0.37
13   -0.41
14   -0.33
15   -0.41
16   -0.41
17   -0.41
18   -0.41
19   -0.41
20   -1.65
21   -1.80
22   -1.53
23   -1.35
24   -1.80
25   -0.19
26   -0.14
28   -0.21
  

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

1. эй, это будет иметь ужасную временную сложность больше, чем O (n ^ 2). Иногда эффективен простой код wring. Утилиты Pandas / python не работают. Это просто итерация строки по всем строкам с сохранением минимума

2. серьезно? вы не видели cummin, это операция O (n)

3. Возможно, я что-то упускаю, но предполагаемый результат OP для индекса 11 должен был быть -0,19, а не -0,34?

4. @anon01 попробуйте проверить время выполнения вашего кода на некоторых фиктивных данных. Я запустил 10 тысяч строк, а время обработки вашего кода составляет 19 секунд. Я запустил линейный цикл, сохраняя минимум, а время обработки составляет 42 мс. Вы должны прочитать о временной сложности 🙂

5. Спасибо! Даже этот фрагмент кода очень близок к тому, что мне нужно, кроме значения в индексе 11, которое должно быть -0,19

Ответ №3:

Простое решение, просто выполните итерацию по столбцу и сохраните минимальное время и заполните значение Nan

 def fill_min(df):
  minx = np.inf
  ans = []
  for val in df['A_Col']:
    if np.isnan(val):
      ans.append(val if np.isinf(minx) else minx)
    else:
      minx = min(minx, val)
      ans.append(val)
  return ans
  

ИСПОЛЬЗУЙТЕ:

 df['A_col_fill'] = fill_min(df)