Оптимизация функции проверки достоверности pandera Polars

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

Вот одна из проверок, в которой я заметил относительно существенные различия между проверками поляров и пандами:

def has_no_conditional_field_conflict(
    grouped_data: pa.PolarsData,
    condition_values: set[str] = {"977"},
    groupby_fields: str = "", 
    separator: str = ";",
) -> pl.LazyFrame:
    start = datetime.now()

    lf = grouped_data.lazyframe
    check_col = pl.col(groupby_fields).str.split(separator)
    val_col = pl.col(grouped_data.key).str.strip()
    
    check_results = (
        (check_col.apply(lambda arr: set(arr).isdisjoint(condition_values), return_dtype=pl.Boolean)) &
        (val_col == "")
    ) | (
        (check_col.apply(lambda arr: not set(arr).isdisjoint(condition_values), return_dtype=pl.Boolean)) &
        (val_col != "")
    )

    rf = lf.with_columns(check_results.alias("check_results")).select("check_results").collect()
    print(f"Processing of has_no_conditional_field_conflict took {(datetime.now() - start).total_seconds()} seconds")
    return rf.lazy()

В полярах эта функция занимает в среднем ~0,1 секунды и используется для многих полей (вызывается 41 раз во время проверки). Общее время проверки 10 000 записей занимает около 8,5 секунд. Если я удалю .collect() и просто передам ленивый кадр с выражениями, общая обработка самой функции составит около 0,0007 секунды, но тогда общий прогон проверки займет около 13 секунд.

При запуске в pandas с использованием groupby и переборе данных groupby (которые при проверках pandera pandas предоставляют dict[value, series]) по одному и тому же набору данных я вижу время функции проверки 0,008 секунды и общую проверку 6 секунд.

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

Обновление. Целью этого является проверка того, находится ли значение поля A (check_col) в наборе Condition_values. Если Поле A присутствует в наборе, то Поле B (val_col) не должно быть пустым. Если Поле A отсутствует в наборе (следовательно, оно непересекающееся), то Поле B должно быть пустым. Поле A может быть либо одним значением, либо строкой значений, разделенных точкой с запятой. Например, поле A может иметь значение 900;977. По умолчанию Condition_value имеет значение {977}, но может представлять собой набор значений.

Что именно делает чек? Похоже, идет тестирование, если "977" нет в списке? то есть .list.contains() Или есть .list.intersection() - Возможно, вы сможете показать реальный пример.

jqurious 14.06.2024 21:08
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
97
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

У Polars пока нет явной функции isdisjoint().

- https://github.com/pola-rs/polars/issues/9908

В качестве обходного пути вы можете проверить длину списка list.set_intersection

df = pl.DataFrame({
    "check_col": ["900;977", "1;977;2", "", "123"], 
    "val_col": ["foo", "", "bar", ""]
})

condition_values = {"977", "1"}

df.with_columns(is_disjoint = 
   pl.col.check_col.str.split(";").list.set_intersection(list(condition_values))
     .list.len() == 0
)
shape: (4, 3)
┌───────────┬─────────┬─────────────┐
│ check_col ┆ val_col ┆ is_disjoint │
│ ---       ┆ ---     ┆ ---         │
│ str       ┆ str     ┆ bool        │
╞═══════════╪═════════╪═════════════╡
│ 900;977   ┆ foo     ┆ false       │
│ 1;977;2   ┆         ┆ false       │
│           ┆ bar     ┆ true        │
│ 123       ┆         ┆ true        │
└───────────┴─────────┴─────────────┘
  • примечание: Polars в настоящее время не принимает объекты set, поэтому мы явно вызываем list() при передаче condition_values

См. мой ответ ниже, но ваш первоначальный ответ на функции списка заставил меня задуматься о том, как переосмыслить мой подход к полярным выражениям. Итак, на выходных я размышлял над этим и придумал следующее. Сократите время обработки этой функции значительно ниже того, что было раньше. Сейчас в среднем это занимает 0,001 секунды. Спасибо!

Cthulhujr 17.06.2024 15:21

Вот что я придумал:

    start = datetime.now()

    lf = grouped_data.lazyframe
    check_col = (pl.col(groupby_fields).str.split(separator).list.set_intersection(list(condition_values)).list.lengths() == 0).alias("check_col")
    val_col = (pl.col(grouped_data.key).str.strip_chars().str.n_chars() == 0).alias("val_col")
    rf = lf.with_columns([check_col, val_col]).select(["check_col", "val_col"]).collect()
    rf = rf["check_col"] ^ rf["val_col"]
    
    print(f"Processing of has_no_conditional_field_conflict took {(datetime.now() - start).total_seconds()} seconds")
    return pl.DataFrame(~rf).lazy()

Понял, что у меня возникла ситуация, когда проверка проходит только в том случае, если пересечение пусто, строка должна быть пустой. Если пересечение не пусто, строка не может быть пустой. Итак, bool поместил в столбец два результата и XORd.

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