Поляры вычитают массив numpy 1xn из n столбцов

Я борюсь с поляками. У меня есть фрейм данных и массив numpy. Я хотел бы их вычесть.

import polars as pl
import pandas as pd

df = pl.DataFrame(np.random.randn(6, 4), schema=['#', 'x', 'y', 'z'])

arr = np.array([-10, -20, -30])


df.select(
    pl.col(r'^[x|y|z]$')
).apply(
    lambda x: np.array(x) - arr
)

shape: (6, 3)
┌───────────┬───────────┬───────────┐
│ column_0  ┆ column_1  ┆ column_2  │
│ ---       ┆ ---       ┆ ---       │
│ f64       ┆ f64       ┆ f64       │
╞═══════════╪═══════════╪═══════════╡
│ 10.143819 ┆ 21.875335 ┆ 29.682364 │
│ null      ┆ null      ┆ null      │
│ null      ┆ null      ┆ null      │
│ null      ┆ null      ┆ null      │
│ null      ┆ null      ┆ null      │
│ null      ┆ null      ┆ null      │
└───────────┴───────────┴───────────┘

Так что теперь вычитание применяется только к первой строке.

Но если я попытаюсь вычислить норму, например, то это работает для каждой строки:

df.select(
    pl.col(r'^[x|y|z]$')
).apply(
    lambda x: np.sum((np.array(x) - arr)**2)**0.5
)
shape: (6, 1)
┌───────────┐
│ apply     │
│ ---       │
│ f64       │
╞═══════════╡
│ 38.242255 │
│ 37.239545 │
│ 38.07624  │
│ 36.688312 │
│ 38.419194 │
│ 36.262196 │
└───────────┘

# check if it is correct:
np.sum((df.to_pandas()[['x', 'y', 'z']].to_numpy() - arr)**2, axis=1) ** 0.5
>>> array([38.24225488, 37.23954478, 38.07623986, 36.68831161, 38.41919409,
       36.2621962 ])

В пандах это можно сделать так:

df.to_pandas()[['x', 'y', 'z']] - arr

x   y   z
0   10.143819   21.875335   29.682364
1   10.360651   21.116404   28.871060
2   9.777666    20.846593   30.325185
3   9.394726    19.357053   29.716592
4   9.223525    21.618511   30.390805
5   9.751234    21.667080   27.393393

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

df.select(
    pl.col('x') - arr[0], pl.col('y') - arr[1], pl.col('z') - arr[2]
)

Измените лямбда-функцию на lambda x: tuple(np.array(x) - arr) и повторите попытку.

Timus 22.02.2023 13:34

Нет, это не сработало, он вычел arr[0] из всех столбцов.

3dSpatialUser 22.02.2023 13:51
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Потяните за рычаг выброса энергососущих проектов
Потяните за рычаг выброса энергососущих проектов
На этой неделе моя команда отменила проект, над которым я работал. Неделя усилий пошла насмарку.
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Привет, люди RPA, это снова я и я несу подарки! В очередном моем приключении о том, как создавать ботов для облегчения рутины. Вот, думаю, стоит...
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
1
2
89
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете сопоставить вывод pandas

In [15]: df.to_pandas()[['x', 'y', 'z']] - arr
Out[15]:
           x          y          z
0  10.342991  21.258934  29.083287
1  10.136803  21.543558  28.168207
2  11.900141  19.557348  29.490541
3   9.192346  19.498689  28.195094
4   9.219745  20.330358  29.005278
5  11.853378  19.458095  30.357041

С

In [17]: df.select([pl.col(col)-arr[i] for i, col in enumerate(['x', 'y', 'z'])])
Out[17]:
shape: (6, 3)
┌───────────┬───────────┬───────────┐
│ x         ┆ y         ┆ z         │
│ ---       ┆ ---       ┆ ---       │
│ f64       ┆ f64       ┆ f64       │
╞═══════════╪═══════════╪═══════════╡
│ 10.342991 ┆ 21.258934 ┆ 29.083287 │
│ 10.136803 ┆ 21.543558 ┆ 28.168207 │
│ 11.900141 ┆ 19.557348 ┆ 29.490541 │
│ 9.192346  ┆ 19.498689 ┆ 28.195094 │
│ 9.219745  ┆ 20.330358 ┆ 29.005278 │
│ 11.853378 ┆ 19.458095 ┆ 30.357041 │
└───────────┴───────────┴───────────┘

Привет, спасибо за ваш вклад. Ваше первое решение df.select([pl.col('x')+10, pl.col('y')+20, pl.col('z')+30]) уже было опубликовано в моем вопросе. Меня не удовлетворяет «простота» использования поляр, особенно когда массивы становятся больше. Второе решение вполне работоспособно, но, на мой взгляд, оно не использует простой выбор полярных выражений с помощью регулярных выражений. Но спасибо за ваш ответ, но, к сожалению, я не думаю, что это действительно решает проблему python-polars, но это хороший обходной путь.

3dSpatialUser 22.02.2023 11:33

Я увидел на короткое время ответ, который искал, но комментарий удален.

Решение состояло в том, чтобы вернуть кортеж:

df.select(
    pl.col(r'^[x|y|z]$')
).apply(
    # lambda x: np.array(x) - arr  # old code
    lambda x: tuple(np.array(x) - arr)  # new code
)

Ага, извините, я запутался и перепроверил результат :(

Timus 22.02.2023 13:34

Хорошо, это работает для варианта select. Не работает с with_columns например: df.with_columns( pl.col('^[x|y|z]$').apply(lambda x: tuple(np.array(x) - arr)) ) не получилось...

3dSpatialUser 22.02.2023 13:39

Да. И мне не нравится решение (поэтому я только добавил комментарий).

Timus 22.02.2023 13:42
apply будет медленнее, вы, вероятно, захотите избежать этого решения, если сможете
ignoring_gravity 22.02.2023 13:51

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

3dSpatialUser 22.02.2023 13:58

Да, верно - Дин МакГрегор очень хорошо объясняет, почему вы, вероятно, хотите избежать apply, если можете

ignoring_gravity 22.02.2023 14:51
Ответ принят как подходящий

В этом вопросе происходит несколько вещей.

Во-первых, вы действительно не хотите использовать apply, если вы не делаете что-то, что является пользовательской функцией Python.

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

На самом деле нет полярного способа делать то, что вы хотите. Когда polars увидит pl.col(r'^[x|y|z]$').expr, он идентифицирует каждый столбец, который соответствует регулярному выражению, а затем будет поток, выполняющий работу остальной части выражения. Выражение не знает, в каком порядке оно было. Он знает только, каковы его данные и что он должен делать. Поэтому вы ничего не можете добавить в expr, чтобы он знал, к какому элементу массива обращаться.

Чтобы получить то, что вы хотите, вам нужно сделать что-то вроде @ignoring_gravity, но вы можете использовать модуль re.

import re
df.select(pl.col(col)-arr[i] 
          for i, col in enumerate(filter(re.compile(r'^[x|y|z]$').match, df.columns)))

Другой вариант, который позволяет избежать импорта re:

res = df.select(
    pl.col(col) - c
    for col, c in zip(df.select(pl.col(r'^[x|y|z]$')).columns, arr)
)

Это немного медленнее для очень маленьких кадров данных (я думаю, потому что в этом случае преобладает скорость регулярных выражений), но одинаково быстро для больших.

В вашем почтовом индексе мне интересно, будет ли выполнение df.filter(False).select(pl.col(r'^[x|y|z]$')).columns улучшением производительности, поскольку оно не позволит ему возвращать строки внутри (возможно, это все равно происходит, я не уверен). В любом случае мне нравится этот трюк. Вы также можете сделать pl.DataFrame([x-y for x,y in zip(df.select(pl.col(r'^[x|y|z]$')), arr)])

Dean MacGregor 23.02.2023 13:14

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