Я пытаюсь выполнить некоторые агрегации, хотя мне нравились поляры, но есть определенные вещи, которые я не могу выполнить. Вот мой подход и вопрос для справки.
import polars as pl
import polars.selectors as cs
import numpy as np
data = pl.DataFrame({'x': ['a', 'b', 'a', 'b', 'a', 'a', 'a', 'b', 'a'],
'y': [2, 3, 4, 5, 6, 7, 8, 9, 10],
'z': [4, np.nan, np.nan, 8,1, 1, 3, 4, 0],
'm' : [np.nan, 8, 1, np.nan, 3, 4, 8, 7, 1]})
У меня есть фрейм данных, как указано выше. Вот мои вопросы и соответствующая попытка
Пытаться:
data.group_by('x').agg(pl.all().mean(),
pl.all().sum())
print(data.select(pl.col('m').median())) ## line 1
print(data.select(pl.col('m').mean())) ## line 2
Если я заменю np.nan
на None
, вычисление среднего значения будет работать нормально в «строке 2» приведенного выше кода, почему?
почему это не работает? Я получаю ошибку вычислений, в которой говорится: расширение более чем на один col
не разрешено, что это на самом деле означает? По сути, я хотел отфильтровать любые строки, отсутствующие в обоих столбцах.
data.filter(pl.col(['z']).is_nan() | pl.col(['m']).is_nan())
NaN
в нескольких столбцах за один раз, я написал этот код, и он тоже работает, но он неуклюжий, есть ли лучший способ?mean_impute = np.nanmean(data.select(pl.col(['z', 'm'])).to_numpy(), axis=0)
def replace_na(data, colname, i):
return data.with_columns(pl.when(pl.col(colname).is_nan()
).then(mean_impute[i]).otherwise(pl.col(colname)).alias(colname)).select(colname).to_numpy().flatten()
data.with_columns(z = replace_na(data, 'z', 0),
m = replace_na(data, 'm', 1))
Спасибо, что прочитали вопрос и ответили. Я не хочу помещать дублирующую запись в SO. Я понимаю правила, поэтому сообщите мне, если это в каком-либо смысле дубликаты. Я бы с радостью удалил их. Но я не смог решить некоторые из них или написать решение, которое могло быть не очень хорошим. Еще раз спасибо !!!
запущенная версия Python: '3.10.9'
версия бегущей поляры: '0.20.31'
Как рассчитать несколько сводок по нескольким столбцам (я получаю ошибку дублирования столбцов, как это исправить?)
Переименуйте выражения, чтобы избежать ошибки дублирования столбцов. Один из способов — использовать выражение с префиксом :
In [7]: data.group_by('x').agg(pl.all().mean(), pl.all().sum().prefix('sum_'))
Out[7]:
shape: (2, 7)
┌─────┬──────────┬─────┬─────┬───────┬───────┬───────┐
│ x ┆ y ┆ z ┆ m ┆ sum_y ┆ sum_z ┆ sum_m │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 ┆ f64 ┆ i64 ┆ f64 ┆ f64 │
╞═════╪══════════╪═════╪═════╪═══════╪═══════╪═══════╡
│ a ┆ 6.166667 ┆ NaN ┆ NaN ┆ 37 ┆ NaN ┆ NaN │
│ b ┆ 5.666667 ┆ NaN ┆ NaN ┆ 17 ┆ NaN ┆ NaN │
└─────┴──────────┴─────┴─────┴───────┴───────┴───────┘
почему медиана считается действительным значением, а среднее — нет? Возможный ответ: это потому, что медиана рассчитывается путем сортировки и выбора среднего значения, и поскольку в этом случае центральное значение не равно нулю, следовательно, оно действительно (не уверен, что это причина)
Polars обрабатывает нули и NaN по-разному - эта статья здесь объясняет это лучше - TLDR - замените NaN на нули:
data.select(pl.col('m').fill_nan(None).mean())
Out[12]:
shape: (1, 1)
┌──────────┐
│ m │
│ --- │
│ f64 │
╞══════════╡
│ 4.571429 │
└──────────┘
Чтобы заполнить несколько столбцов, используйте fill_nan
:
In [16]: data.select(cs.numeric().as_expr().fill_nan(None).mean())
Out[16]:
shape: (1, 3)
┌─────┬─────┬──────────┐
│ y ┆ z ┆ m │
│ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f64 │
╞═════╪═════╪══════════╡
│ 6.0 ┆ 3.0 ┆ 4.571429 │
└─────┴─────┴──────────┘
Агрегации будут использовать имя входного столбца, поэтому вам понадобится псевдоним, если вы выполняете несколько агрегатов для одного или нескольких столбцов.
Этого можно достичь с помощью методов .name.suffix
, .name.prefix
и .name.map
здесь для одновременного переименования выражений из нескольких столбцов (например, pl.all()
). .alias
— еще один вариант, если вы просто переименовываете один столбец.
# Assuming `data` from the question is already defined
# Solution - multiple aggregations on a single column
data.group_by('x').agg(
pl.col("y").sum().alias("y_sum"),
pl.col("y").mean().alias("y_mean"),
)
# Solution - multiple aggregations on multiple columns
data.group_by('x').agg(
pl.all().sum().name.suffix("_sum"),
pl.all().mean().name.suffix("_mean"),
)
Выражения являются составными, поэтому вы можете использовать псевдоним, суффикс, префикс и т. д. в соответствии с вашими потребностями.
Следует провести важное различие между NaN
и null
в Полярных. null
предназначен для отсутствующих данных, а NaN
— это тип данных с плавающей запятой. В вашем data
DataFrame вы можете видеть, что столбцы z
и m
преобразуются в числа с плавающей запятой из-за присутствия NaN
.
Если вы хотите представить недостающие данные, преобразуйте NaN
в null
или определите данные с помощью None
вместо np.nan
. Подробнее здесь.
NaN
распространяются на среднее значение. Я подозреваю, что ваш возможный ответ, касающийся медианы, верен. Как видите, это распространенное явление. У Numpy есть специальная функция для игнорирования NaN
, а панды, вероятно, по умолчанию пропускают NaN
, поскольку в пандах нет null
.
np.mean(np.array([0, 1, 2, np.nan])) # NaN
np.nanmean(np.array([0, 1, 2, np.nan])) # 1.0
pd.DataFrame([0, 1, 2, np.nan]).mean() # 1.0
pd.DataFrame([0, 1, 2, np.nan]).mean(skipna=False) # NaN
pl.DataFrame([0, 1, 2, np.nan]).mean() # NaN
pl.DataFrame([0, 1, 2, None]).mean() # 1.0
Как вы заметили, поляры не распространяются null
до среднего значения, и это также предпочтительный способ представления недостающих данных.
Похоже, квадратные скобки внутри col
вызывают
ComputeError: expanding more than one `col` is not allowed
По моей интуиции, основанной на ошибке, квадратные скобки указывают Polars на необходимость расширения выражения до более чем одного столбца, что недопустимо при выполнении логического или другого столбца. Удаление квадратных скобок исправляет ситуацию.
data.filter(pl.col('z').is_nan() | pl.col('m').is_nan())
Код, который вы опубликовали, похоже, заменяет NaN
на среднее значение.
Если вы просто хотите заменить NaN
на null
, это поможет.
data.with_columns(pl.col("z", "m").fill_nan(None))
И если вы хотите заменить NaN
на среднее значение, все равно предпочтительнее заменять NaN
на null
, и тогда вам не придется выполнять нумерацию, чтобы вычислить среднее значение.
data.with_columns(
pl.when(pl.col("z", "m").is_nan())
# Fill the NaNs with null, then calculate the mean
.then(pl.col("z", "m").fill_nan(None).mean())
# Otherwise keep the original value
.otherwise(pl.col("z", "m"))
)
Я думаю, что существующие ответы хорошо отвечают на ваши вопросы
Просто хочу добавить, что если вы хотите иметь более общий вариант добавления нескольких агрегатов, вы можете использовать синтаксические сахарные версии агрегатов и сделать их расширяемыми:
# list of tuples (aggregate, suffix)
aggregates = [(pl.sum, '_sum'), (pl.median, '_median')]
(
data
.group_by('x')
.agg(
f_agg("*").name.suffix(f_suf) for f_agg, f_suf in aggregates
)
)
┌─────┬───────┬───────┬───────┬──────────┬──────────┬──────────┐
│ x ┆ y_sum ┆ z_sum ┆ m_sum ┆ y_median ┆ z_median ┆ m_median │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
╞═════╪═══════╪═══════╪═══════╪══════════╪══════════╪══════════╡
│ b ┆ 17 ┆ NaN ┆ NaN ┆ 5.0 ┆ 8.0 ┆ 8.0 │
│ a ┆ 37 ┆ NaN ┆ NaN ┆ 6.5 ┆ 2.0 ┆ 3.5 │
└─────┴───────┴───────┴───────┴──────────┴──────────┴──────────┘
Хороший! Я сохраню это под рукой, если мне понадобятся еще какие-то динамически создаваемые агрегаты.