Я тестирую переключение на поляры с панд и сталкиваюсь с проблемами производительности, которых не ожидал. Надеюсь, что это просто проблема незнания действительно оптимизированного способа проверки данных с использованием ленивых фреймов.
Вот одна из проверок, в которой я заметил относительно существенные различия между проверками поляров и пандами:
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}, но может представлять собой набор значений.
У 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 │
└───────────┴─────────┴─────────────┘
set
, поэтому мы явно вызываем list()
при передаче condition_values
См. мой ответ ниже, но ваш первоначальный ответ на функции списка заставил меня задуматься о том, как переосмыслить мой подход к полярным выражениям. Итак, на выходных я размышлял над этим и придумал следующее. Сократите время обработки этой функции значительно ниже того, что было раньше. Сейчас в среднем это занимает 0,001 секунды. Спасибо!
Вот что я придумал:
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.
Что именно делает чек? Похоже, идет тестирование, если
"977"
нет в списке? то есть .list.contains() Или есть.list.intersection()
- Возможно, вы сможете показать реальный пример.