Объединение нескольких столбцов в полярах с пропущенными значениями

Я пытаюсь выполнить некоторые агрегации, хотя мне нравились поляры, но есть определенные вещи, которые я не могу выполнить. Вот мой подход и вопрос для справки.

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]})

У меня есть фрейм данных, как указано выше. Вот мои вопросы и соответствующая попытка

  1. Как рассчитать несколько сводок по нескольким столбцам (я получаю ошибку дублирования столбцов, как это исправить?)

Пытаться:

data.group_by('x').agg(pl.all().mean(),
                       pl.all().sum())

  1. почему медиана считается действительным значением, а среднее — нет? Возможный ответ: это потому, что медиана рассчитывается путем сортировки и выбора среднего значения, и поскольку в этом случае центральное значение не равно нулю, следовательно, оно действительно (не уверен, что это причина)
print(data.select(pl.col('m').median())) ## line 1
print(data.select(pl.col('m').mean())) ## line 2

  1. Если я заменю np.nan на None, вычисление среднего значения будет работать нормально в «строке 2» приведенного выше кода, почему?

  2. почему это не работает? Я получаю ошибку вычислений, в которой говорится: расширение более чем на один col не разрешено, что это на самом деле означает? По сути, я хотел отфильтровать любые строки, отсутствующие в обоих столбцах.


data.filter(pl.col(['z']).is_nan() | pl.col(['m']).is_nan())
  1. Как заменить 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'

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
121
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Как рассчитать несколько сводок по нескольким столбцам (я получаю ошибку дублирования столбцов, как это исправить?)

Переименуйте выражения, чтобы избежать ошибки дублирования столбцов. Один из способов — использовать выражение с префиксом :

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 │
└─────┴─────┴──────────┘
Ответ принят как подходящий
1:

Агрегации будут использовать имя входного столбца, поэтому вам понадобится псевдоним, если вы выполняете несколько агрегатов для одного или нескольких столбцов.

Этого можно достичь с помощью методов .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"),
)

Выражения являются составными, поэтому вы можете использовать псевдоним, суффикс, префикс и т. д. в соответствии с вашими потребностями.

2 и 3:

Следует провести важное различие между 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 до среднего значения, и это также предпочтительный способ представления недостающих данных.

4:

Похоже, квадратные скобки внутри col вызывают

ComputeError: expanding more than one `col` is not allowed

По моей интуиции, основанной на ошибке, квадратные скобки указывают Polars на необходимость расширения выражения до более чем одного столбца, что недопустимо при выполнении логического или другого столбца. Удаление квадратных скобок исправляет ситуацию.

data.filter(pl.col('z').is_nan() | pl.col('m').is_nan())
5:

Код, который вы опубликовали, похоже, заменяет 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      │
└─────┴───────┴───────┴───────┴──────────┴──────────┴──────────┘

Хороший! Я сохраню это под рукой, если мне понадобятся еще какие-то динамически создаваемые агрегаты.

Henry Harbeck 11.06.2024 11:39

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