Как выполнить разделение/объединение/разворот с помощью Python и поляров?Python

Программы на Python
Anonymous
Как выполнить разделение/объединение/разворот с помощью Python и поляров?

Сообщение Anonymous »

У меня есть проблема преобразования данных, когда исходные данные состоят из «блоков» из трех строк данных, где первая строка обозначает «родителя», а две другие являются связанными дочерними элементами. Минимальный рабочий пример выглядит так:
import polars as pl
df_original = pl.DataFrame(
{
'Order ID': ['A', 'foo', 'bar'],
'Parent Order ID': [None, 'A', 'A'],
'Direction': ["Buy", "Buy", "Sell"],
'Price': [1.21003, None, 1.21003],
'Some Value': [4, 4, 4],
'Name Provider 1': ['P8', 'P8', 'P8'],
'Quote Provider 1': [None, 1.1, 1.3],
'Name Provider 2': ['P2', 'P2', 'P2'],
'Quote Provider 2': [None, 1.15, 1.25],
'Name Provider 3': ['P1', 'P1', 'P1'],
'Quote Provider 3': [None, 1.0, 1.4],
'Name Provider 4': ['P5', 'P5', 'P5'],
'Quote Provider 4': [None, 1.0, 1.4]
}
)

В действительности существует до 15 провайдеров (то есть до 30 столбцов), но для примера они не обязательны.
Мы бы хотели хотелось бы преобразовать это в формат, в котором каждая строка представляет котировку покупки и продажи одного поставщика для этого родителя. Желаемый результат следующий:
df_desired = pl.DataFrame(
{
'Order ID': ['A', 'A', 'A', 'A'],
'Parent Direction': ['Buy', 'Buy', 'Buy', 'Buy'],
'Price': [1.21003, 1.21003, 1.21003, 1.21003],
'Some Value': [4, 4, 4, 4],
'Name Provider': ['P8', 'P2', 'P1', 'P5'],
'Quote Buy': [1.1, 1.15, 1.0, 1.0],
'Quote Sell': [1.3, 1.25, 1.4, 1.4],
}
)
df_desired

Однако мне сложно сделать это в полярах.
Мой первый подход заключался в разделении данных на родителей и детей, а затем объединении их вместе по соответствующим идентификаторам:
df_parents = (
df_original
.filter(pl.col("Parent Order ID").is_null())
.drop(columns=['Parent Order ID'])
)
df_ch = (
df_original
.filter(pl.col("Parent Order ID").is_not_null())
.drop(columns=['Price', 'Some Value'])
)

ch_buy = df_ch.filter(pl.col("Direction") == 'Buy').drop(columns=['Direction'])
ch_sell = df_ch.filter(pl.col("Direction") == 'Sell').drop(columns=['Direction'])

df_joined = (
df_parents
.join(ch_buy, left_on='Order ID', right_on='Parent Order ID', suffix="_Buy")
.join(ch_sell, left_on='Order ID', right_on='Parent Order ID', suffix="_Sell")
# The Name and Quote columns in the parent are all empty, so they can go, buy they had to be there for the suffix to work for the first join
.drop(columns=[f'Name Provider {i}' for i in range(1, 5)])
.drop(columns=[f'Quote Provider {i}' for i in range(1, 5)])
)

Но это по-прежнему оставляет путаницу, когда вам каким-то образом приходится разбивать это на четыре строки, а не на восемь, как вы могли бы легко сделать с помощью .unpivot(). Есть какие-нибудь советы о том, как лучше всего к этому подойти? Я упускаю здесь какой-то очевидный метод?
РЕДАКТИРОВАТЬ:
Добавлен немного больший пример фрейма данных с двумя родительскими заказами и их дочерними элементами (реальный в наборе данных их около 50 тысяч):
df_original_two_orders = pl.DataFrame(
{
'Order ID': ['A', 'foo', 'bar', 'B', 'baz', 'rar'], # Two parent orders
'Parent Order ID': [None, 'A', 'A', None, 'B', 'B'],
'Direction': ["Buy", "Buy", "Sell", "Sell", "Sell", "Buy"], # Second parent has different direction
'Price': [1.21003, None, 1.21003, 1.1384, None, 1.1384],
'Some Value': [4, 4, 4, 42, 42, 42],
'Name Provider 1': ['P8', 'P8', 'P8', 'P2', 'P2', 'P2'],
'Quote Provider 1': [None, 1.1, 1.3, None, 1.10, 1.40],
# Above, 1.10 corresponds to Buy for order A for to Sell for order B - depends on Direction
'Name Provider 2': ['P2', 'P2', 'P2', 'P1', 'P1', 'P1'],
'Quote Provider 2': [None, 1.15, 1.25, None, 1.11, 1.39],
'Name Provider 3': ['P1', 'P1', 'P1', 'P3', 'P3', 'P3'],
'Quote Provider 3': [None, 1.0, 1.4, None, 1.05, 1.55],
'Name Provider 4': ['P5', 'P5', 'P5', None, None, None],
'Quote Provider 4': [None, 1.0, 1.4, None, None, None]
}
)

Я думаю, что это немного более репрезентативно для реального мира, поскольку здесь имеется несколько родительских заказов и не все столбцы поставщиков заполняются для всех заказов, при этом раздражающая бизнес-логика остается в стороне.
Правильный вывод для этого примера следующий:
df_desired_two_parents = pl.DataFrame(
{
'Order ID': ['A']*4 + ['B'] * 3,
'Parent Direction': ['Buy']*4 + ['Sell'] * 3,
'Price': [1.21003] * 4 + [1.1384] * 3,
'Some Value': [4] * 4 + [42] * 3,
'Name Provider': ['P8', 'P2', 'P1', 'P5', 'P2', 'P1', 'P3'],
'Quote Buy': [1.1, 1.15, 1.0, 1.0, 1.40, 1.39, 1.55], # Note the last three values are the "second" values in the original column now because the parent order was 'Sell'
'Quote Sell': [1.3, 1.25, 1.4, 1.4, 1.10, 1.11, 1.05],
}
)


Подробнее здесь: https://stackoverflow.com/questions/745 ... and-polars

Вернуться в «Python»