Я хочу переслать некоторые значения в мой фрейм данных pandas с ограничением. Однако предел должен заполнять только группы наноразмеров, в которых количество непрерывных нанометров меньше или равно пределу. Вот пример,
Создайте df с недостающими данными,
import numpy as np
import pandas as pd
df = pd.DataFrame(
{'val': [1, 1, np.nan, np.nan, 2, 3, np.nan, np.nan, np.nan, np.nan, 1, 1]}
)
print(df)
val
0 1.0
1 1.0
2 NaN
3 NaN
4 2.0
5 3.0
6 NaN
7 NaN
8 NaN
9 NaN
10 1.0
11 1.0
Теперь, если мы заполним его, все наны будут заполнены на 2 шага вперед,
print(df.ffill(limit=2))
val
0 1.0
1 1.0
2 1.0
3 1.0
4 2.0
5 3.0
6 3.0 #
7 3.0 #
8 NaN
9 NaN
10 1.0
11 1.0
Те чтения (выше) с # fill, когда я этого не хочу. Я бы хотел получить следующее,
print(df.ffill(limit=2, dont_fill_any_nan_gaps_bigger_than_limit=True))
val
0 1.0
1 1.0
2 1.0
3 1.0
4 2.0
5 3.0
6 NaN #
7 NaN #
8 NaN
9 NaN
10 1.0
11 1.0
Конечно, это не обязательно должен быть единственный параметр в ffill, но что-то с таким же эффектом.






Вы можете безоговорочно использовать ffill при условии, что вы не заполните его после использования pd.Series.mask / pd.Series.where / np.where.
v = df.value.isna() # df.value.isnull()
df = df.ffill(limit=2).mask(
v.groupby(v.ne(v.shift()).cumsum()).transform('size').gt(2)
)
value
0 1.0
1 1.0
2 1.0
3 1.0
4 2.0
5 3.0
6 NaN
7 NaN
8 NaN
9 NaN
10 1.0
11 1.0
Я видел, как был представлен isna. Рекомендуется ли он вместо isnull?
@josh Нет, это разные имена, но оба делают одно и то же.
Извините, они делают то же самое. Я хотел спросить, рекомендуется ли использовать isna для уточнения / является ли isnull устаревшим?
@josh Для переносимости используйте isnull, isna, я думаю, был введен только в версии 0.21. На данный момент ни один из них не является устаревшим.
Создайте маску для фильтрации всех строк с NaN с размером больше, чем 2 на groupby и transformsize, и примените ffill только для отфильтрованных строк с условием инвертирования по ~:
a = df['value'].isna()
a = a.ne(a.shift()).cumsum()
m = (a.groupby(a).transform('size') > 2)
df[~m] = df[~m].ffill(limit=2)
print (df)
value
0 1.0
1 1.0
2 1.0
3 1.0
4 2.0
5 3.0
6 NaN
7 NaN
8 NaN
9 NaN
10 1.0
11 1.0
Я черпал вдохновение в обоих отличных ответах, а также в этом отвечать от jezral до моего предыдущего вопрос, чтобы применить это ко всему DataFrame.
Я сделал это для того, чтобы обработать весь DataFrame за один раз и чтобы каждый столбец ffill содержал недостающие значения в своих соответствующих столбцах (с моим дополнительным ограничением).
Настройте DataFrame,
df = pd.DataFrame(
{'val1': [1, 1, np.nan, np.nan, 2, 3, np.nan, np.nan, np.nan, np.nan, 1, 1],
'val2': [1, 2, np.nan, np.nan, 2, 4, 4, np.nan, np.nan, np.nan, np.nan, 2]}
)
print(df)
val1 val2
0 1.0 1.0
1 1.0 2.0
2 NaN NaN
3 NaN NaN
4 2.0 2.0
5 3.0 4.0
6 NaN # 4.0
7 NaN # NaN #
8 NaN # NaN #
9 NaN # NaN #
10 1.0 NaN #
11 1.0 2.0
Теперь сделайте mask и ffill,
mask_df = (
df.isnull()
.groupby([df.notnull().all(axis=1).cumsum()])
.rank(method='max') - 1
).gt(2)
df = df.ffill().mask(mask_df)
print(df)
val1 val2
0 1.0 1.0
1 1.0 2.0
2 1.0 2.0
3 1.0 2.0
4 2.0 2.0
5 3.0 4.0
6 NaN # 4.0
7 NaN # NaN #
8 NaN # NaN #
9 NaN # NaN #
10 1.0 NaN #
11 1.0 2.0
Объяснение
Мы groupby, используя кумулятивную сумму значений, отличных от nan. Это означает, что значения nan сгруппированы вместе. Если мы возьмем максимальное значение rank из них, мы получим длину последовательности nan + 1. Теперь мы просто используем функцию mask, как показано cᴏʟᴅsᴘᴇᴇᴅ's отвечать.
Если установить df.iloc[9,1] = 23, это перестает работать, если вы хотите обрабатывать каждый столбец df отдельно. Мой текущий обходной путь - определить функцию маски def masker(df, limit): return (df.isna().groupby([df.notna().cumsum()]).rank(method='max') - 1).gt(limit) и применить эту функцию к столбцам: mask_df = df_ext.apply(masker, axis=0, args=(limit,)). Я думаю, это довольно медленно для больших фреймов данных. Есть ли способ сделать это напрямую без подачи заявки?
@ Scotty1 - Я не могу понять, почему df.iloc[9,1] = 23 ломает это. Может быть, лучше лечить причину, а не симптомы, но я посмотрю на это.
@josh [df.notnull().all(axis=1).cumsum()] с axis=1 в качестве аргумента groupby рассматривает только строки, которые все являются nan. Но я не смог применить к этому векторизованный подход, у меня сработало только применение. К сожалению, использование этого метода маскирования с применением на фреймах данных среднего размера ужасно медленное. Но честно говоря: я плохо разбираюсь в groupby и т. д., Поэтому я совершенно уверен, что есть лучший подход.
Я просто остановил свой тестовый прогон на df формы (2600000, 80) через 45 минут. Должен быть способ векторизовать это ...
@ Scotty1- извини, теперь я слежу. Это будет сложно, но это действительно хороший вопрос, поэтому стоит создать для него новый пост.
@josh Хорошо, завтра я отвечу на следующий вопрос и опубликую ссылку здесь, в комментариях.
Сразу понял, что наполовину закончил титул. Я добавил название, но, пожалуйста, предложите лучший. Не знал, как это описать ...