Polars: панды, эквивалентные выбору имен столбцов из списка

У меня есть два DataFrames в полярах: один описывает метаданные, а другой — фактические данные (LazyFrames используются, поскольку фактические данные больше):

import polars as pl
df = pl.LazyFrame(
    {
        "ID": ["CX1", "CX2", "CX3"],
        "Sample1": [1, 1, 1],
        "Sample2": [2, 2, 2],
        "Sample3": [4, 4, 4],
    }
)

df_meta = pl.LazyFrame(
    {
        "sample": ["Sample1", "Sample2", "Sa,mple3", "Sample4"],
        "qc": ["pass", "pass", "fail", "pass"]
    }
)

Мне нужно выбрать столбцы в df для образцов, которые прошли проверку qc, используя информацию в df_meta. Как видите, у df_meta есть дополнительная выборка, которая, конечно, нас не интересует, поскольку она не является частью наших данных.

В пандах я бы сделал (не очень элегантно, но выполняет свою работу):

df.loc[:, df.columns.isin(df_meta.query("qc == 'pass'")["sample"])]

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

Я пробовал:

df.with_context(
   df_meta.filter(pl.col("qc") == "pass").select(pl.col("sample").alias("meta_ids"))
).with_columns(
    pl.all().is_in("meta_ids")
).collect()

Однако это вызывает исключение:

InvalidOperationError: `is_in` cannot check for String values in Int64 data

Я предполагаю, что он проверяет содержимое столбцов, но меня интересуют имена столбцов.

Я также пробовал:

meta_ids = df_meta.filter(pl.col("qc") == "pass").get_column("sample")
df.select(pl.col(meta_ids))

но, как и ожидалось, возникает исключение, поскольку в первом dataFrame не учтен один образец:

ColumnNotFoundError: Sample4

Каков был бы правильный способ сделать это?

Просто примечание: with_context устарел: github.com/pola-rs/polars/pull/16860

jqurious 27.06.2024 16:07
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
76
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Либо у вас неправильный ввод (Sample4 нет во входном DataFrame), либо вы хотите выбирать только столбцы, которые имеют pass в df_meta DataFrame И также существуют в df DataFrame.

passing_cols = df_meta.filter(pl.col("qc") == "pass").select(pl.col.sample).collect().to_series()

df.select(list(set(df.columns) & set(passing_cols))).collect()

┌─────────┬─────────┐
│ Sample1 ┆ Sample2 │
│ ---     ┆ ---     │
│ i64     ┆ i64     │
╞═════════╪═════════╡
│ 1       ┆ 2       │
│ 1       ┆ 2       │
│ 1       ┆ 2       │
└─────────┴─────────┘

Предложенное решение правильное, но кажется немного неуклюжим. Более глубокий взгляд на документацию API показал, что функция by_name из API селекторов имеет параметр (require_all), который соответствует этой цели:

import polars.selectors as cs

meta_ids = df_meta.filter(pl.col("qc") == "pass").get_column("sample")
print(df.select(cs.by_name(meta_ids, require_all=False)))

shape: (3, 2)
┌─────────┬─────────┐
│ Sample1 ┆ Sample2 │
│ ---     ┆ ---     │
│ i64     ┆ i64     │
╞═════════╪═════════╡
│ 1       ┆ 2       │
│ 1       ┆ 2       │
│ 1       ┆ 2       │
└─────────┴─────────┘

Просто обратите внимание: это не работает с входными данными вашего примера - потому что у вас есть LazyFrame в качестве входных данных, поэтому вам, возможно, придется немного изменить входные данные или синтаксис. Вот почему я не использовал get_column, а использовал collect одного столбца и вместо него to_series.

Roman Pekar 27.06.2024 12:13

Ах да, я перебрал несколько вариантов, прежде чем остановился на LazyFrame. У меня нет кода передо мной, но я исправлю ответ завтра.

Einar 27.06.2024 18:04
Ответ принят как подходящий

Просто чтобы опираться на https://stackoverflow.com/a/78676922/ - я нахожу require_all=False довольно загадочным.

Также можно установить пересечение с помощью селектора cs.all():

>>> meta_ids = df_meta.filter(pl.col("qc") == "pass")["sample"]
>>> cs.all() & cs.by_name(meta_ids)
(cs.all() & cs.by_name('Sample1', 'Sample2', 'Sample4'))
df.select(cs.all() & cs.by_name(meta_ids))
shape: (3, 2)
┌─────────┬─────────┐
│ Sample1 ┆ Sample2 │
│ ---     ┆ ---     │
│ i64     ┆ i64     │
╞═════════╪═════════╡
│ 1       ┆ 2       │
│ 1       ┆ 2       │
│ 1       ┆ 2       │
└─────────┴─────────┘

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

Похожие вопросы