У меня есть следующий код:
import polars as pl
df = pl.DataFrame({
'name': ['CHECK', 'CASH', 'BAR', 'SET'],
'category1': ['AM', 'EU', 'EU', 'AS'],
'category2': ['CA', 'FR', 'DE', 'CX'],
'quantity': [100, -20, 10, -70],
'exposure': [11, -3, 2, 8]
})
FLT_COLS = ['quantity', 'exposure']
OTHER_COLS = [c for c in df.columns if c not in FLT_COLS]
df_temp = df.select(pl.col(FLT_COLS)).sum()\
.with_columns(pl.lit('TOTAL').alias(OTHER_COLS[0]))\
.with_columns([pl.lit('').alias(c) for c in OTHER_COLS[1:]])[df.columns]
pl.concat([df, df_temp])
что дает мне желаемый результат
shape: (5, 5)
┌───────┬───────────┬───────────┬──────────┬──────────┐
│ name ┆ category1 ┆ category2 ┆ quantity ┆ exposure │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ i64 │
╞═══════╪═══════════╪═══════════╪══════════╪══════════╡
│ CHECK ┆ AM ┆ CA ┆ 100 ┆ 11 │
│ CASH ┆ EU ┆ FR ┆ -20 ┆ -3 │
│ BAR ┆ EU ┆ DE ┆ 10 ┆ 2 │
│ SET ┆ AS ┆ CX ┆ -70 ┆ 8 │
│ TOTAL ┆ ┆ ┆ 20 ┆ 18 │
└───────┴───────────┴───────────┴──────────┴──────────┘
То есть добавьте строку, содержащую общую сумму по определенному списку столбцов FLT_COLS
, пометьте другой первый столбец TOTAL
, а затем поместите ""
в оставшиеся несуммированные столбцы.
Есть ли более приятный способ добавить эту строку? Я чувствую, что мой код выглядит очень неуклюже. Мне также не нравится, что мне приходится указывать [df.columns]
, чтобы изменить порядок столбцов, так как это кажется очень неэффективным.
Известно ли name
или вам нужно вывести его динамически? то есть будет ли pl.concat([df, df.sum().fill_null("").with_columns(name=pl.lit("TOTAL"))])
достаточно?
@jqurious Я бы сказал, что это максимально близко к правильному ответу
@RomanPekar Спасибо за отзыв — я не был уверен, упрощаю ли я ситуацию или нет.
В целом это, вероятно, более неуклюже, но касается:
FLT_COLS
df.columns
pl.concat([
df,
df.select(pl.exclude(FLT_COLS)) # pick all columns that aren't FLT_COLS
.select(
pl.first() # this is the first column [that isn't in FLT_COLS]
.first() # this is first row
.str.replace(r".*","TOTAL")) # uses regex to replace the first value
# to TOTAL as workaround for dynamic alias
.hstack( # add columns of sums
df.select(pl.col(FLT_COLS).sum())
)
], how='diagonal' # the diagonal makes any extra columns default None
# and orders them by the first df of the list)
Если вы знаете, что первого столбца нет в FLT_COLS
, вы можете просто сделать
pl.concat([
df,
df.select(
pl.first().first().str.replace(r".*","TOTAL"),
pl.col(FLT_COLS).sum()
)
], how='diagonal')
Для данного DataFrame
, если все числовые столбцы в FLT_COLS
df = pl.DataFrame({
'name': ['CHECK', 'CASH', 'BAR', 'SET'],
'category1': ['AM', 'EU', 'EU', 'AS'],
'category2': ['CA', 'FR', 'DE', 'CX'],
'quantity': [100, -20, 10, -70],
'exposure': [11, -3, 2, 8] })
этот код работает:
df_temp = (
df.select(pl.all())
.sum()
.fill_null(pl.lit(""))
.with_columns(pl.lit("TOTAL").alias(OTHER_COLS[0]))
)
df = pl.concat([df, df_temp])
выход:
shape: (5, 5)
┌───────┬───────────┬───────────┬──────────┬──────────┐
│ name ┆ category1 ┆ category2 ┆ quantity ┆ exposure │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ i64 │
╞═══════╪═══════════╪═══════════╪══════════╪══════════╡
│ CHECK ┆ AM ┆ CA ┆ 100 ┆ 11 │
│ CASH ┆ EU ┆ CA ┆ -20 ┆ -3 │
│ BAR ┆ EU ┆ CA ┆ 10 ┆ 2 │
│ SET ┆ AS ┆ CA ┆ -70 ┆ 8 │
│ TOTAL ┆ ┆ ┆ 20 ┆ 18 │
└───────┴───────────┴───────────┴──────────┴──────────┘
DataFrame
, то сложнее: import polars as pl
import polars.selectors as cs
df = pl.DataFrame({
"name": ["CHECK", "CASH", "BAR", "SET"],
"category1": ["AM", "EU", "EU", "AS"],
"category2": ["CA", "CA", "CA", "CA"],
"category3": [1, 1, 1, 1],
"category4": [1.3, 1.123, 1.1233, 4.232],
"category5": ["2022-01-01", "2022-01-01", "2022-01-01", "2022-01-01"],
"quantity": [100, -20, 10, -70],
"exposure": [11, -3, 2, 8]})
df = df.with_columns(pl.col("category5").str.to_date("%Y-%m-%d"))
FLT_COLS = ["quantity", "exposure"]
ALL_OTHER_NUMERIC_CS = cs.all() - cs.by_name(FLT_COLS) & cs.numeric()
TOTAL_COL = "name"
df_temp = (
df.select(pl.all())
.sum()
.fill_null(pl.lit(""))
.with_columns(
(ALL_OTHER_NUMERIC_CS).replace_strict(
old=(ALL_OTHER_NUMERIC_CS),
new=pl.lit(None),
),
pl.lit("TOTAL").alias(TOTAL_COL),
)
)
df = pl.concat([df, df_temp])
выход:
shape: (5, 8)
┌───────┬───────────┬───────────┬───────────┬───────────┬────────────┬──────────┬──────────┐
│ name ┆ category1 ┆ category2 ┆ category3 ┆ category4 ┆ category5 ┆ quantity ┆ exposure │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ f64 ┆ date ┆ i64 ┆ i64 │
╞═══════╪═══════════╪═══════════╪═══════════╪═══════════╪════════════╪══════════╪══════════╡
│ CHECK ┆ AM ┆ CA ┆ 1 ┆ 1.3 ┆ 2022-01-01 ┆ 100 ┆ 11 │
│ CASH ┆ EU ┆ CA ┆ 1 ┆ 1.123 ┆ 2022-01-01 ┆ -20 ┆ -3 │
│ BAR ┆ EU ┆ CA ┆ 1 ┆ 1.1233 ┆ 2022-01-01 ┆ 10 ┆ 2 │
│ SET ┆ AS ┆ CA ┆ 1 ┆ 4.232 ┆ 2022-01-01 ┆ -70 ┆ 8 │
│ TOTAL ┆ ┆ ┆ null ┆ null ┆ null ┆ 20 ┆ 18 │
└───────┴───────────┴───────────┴───────────┴───────────┴────────────┴──────────┴──────────┘
Не уверен, что это полностью ответы, но:
Уровень кадра .sum()
выдаст null
для всех строковых столбцов:
>>> df.sum()
shape: (1, 5)
┌──────┬───────────┬───────────┬──────────┬──────────┐
│ name ┆ category1 ┆ category2 ┆ quantity ┆ exposure │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ i64 │
╞══════╪═══════════╪═══════════╪══════════╪══════════╡
│ null ┆ null ┆ null ┆ 20 ┆ 18 │
└──────┴───────────┴───────────┴──────────┴──────────┘
Если name
известно, вы можете fill_null()
и concat()
pl.concat([df, df.sum().fill_null("").with_columns(name=pl.lit("TOTAL"))])
shape: (5, 5)
┌───────┬───────────┬───────────┬──────────┬──────────┐
│ name ┆ category1 ┆ category2 ┆ quantity ┆ exposure │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ i64 ┆ i64 │
╞═══════╪═══════════╪═══════════╪══════════╪══════════╡
│ CHECK ┆ AM ┆ CA ┆ 100 ┆ 11 │
│ CASH ┆ EU ┆ FR ┆ -20 ┆ -3 │
│ BAR ┆ EU ┆ DE ┆ 10 ┆ 2 │
│ SET ┆ AS ┆ CX ┆ -70 ┆ 8 │
│ TOTAL ┆ ┆ ┆ 20 ┆ 18 │
└───────┴───────────┴───────────┴──────────┴──────────┘
Или, как в примере, это всегда «первый столбец», вы можете использовать pl.first()
/pl.nth()
, чтобы добавить строку TOTAL
.
pl.concat([
df,
df.sum().fill_null("").with_columns(
pl.nth(0).replace("", "TOTAL")
)
])
Единственное, что вы можете сделать, это df.sum и суммировать только числовые столбцы. например df.loc['Итого'] = df.sum(numeric_only=True). Он поместит Nan для нечисловых полей, которые вы сможете заменить позже.