Search code examples
dataframeinsertpython-polars

Assigning value to a known row and column, update if exists, insert if not (similar to `df.loc['idx', 'col'] = value` in pandas)


Assume the following polars df:

┌────────────┬─────────┬────────────┬─────────┬──────────┬─────────────┐
│ gDate      ┆ pl      ┆ redemption ┆ pc      ┆ ppc      ┆ tval        │
│ ---        ┆ ---     ┆ ---        ┆ ---     ┆ ---      ┆ ---         │
│ date       ┆ f64     ┆ f64        ┆ f64     ┆ f64      ┆ f64         │
╞════════════╪═════════╪════════════╪═════════╪══════════╪═════════════╡
│ 2024-01-30 ┆ 10069.0 ┆ 10068.0    ┆ 10068.0 ┆ 0.07     ┆ 1492.033    │
│ 2024-01-31 ┆ 10082.0 ┆ 10075.0    ┆ 10082.0 ┆ 0.14     ┆ 1236.318    │
│ 2024-02-03 ┆ 10095.0 ┆ 10095.0    ┆ 10095.0 ┆ 0.13     ┆ 2266.253    │
│ 2024-02-04 ┆ 10102.0 ┆ 10102.0    ┆ 10103.0 ┆ 0.08     ┆ 1418.583    │
│ 2024-02-05 ┆ 10110.0 ┆ 10109.0    ┆ 10109.0 ┆ 0.06     ┆ 1940.722    │
│ …          ┆ …       ┆ …          ┆ …       ┆ …        ┆ …           │
│ 2024-03-26 ┆ 10113.0 ┆ 10112.0    ┆ 10112.0 ┆ 0.07     ┆ 1268.171    │
│ 2024-03-27 ┆ 10126.0 ┆ 10119.0    ┆ 10126.0 ┆ 0.14     ┆ 1240.438    │
│ 2024-03-30 ┆ 10149.0 ┆ 10141.0    ┆ 10148.0 ┆ 0.22     ┆ 1317.312    │
│ 2024-04-02 ┆ 10163.0 ┆ 10162.0    ┆ 10162.0 ┆ 0.14     ┆ 1675.402    │
│ 2024-04-03 ┆ 10177.0 ┆ 10169.0    ┆ 10176.0 ┆ 0.137768 ┆ 1546.426394 │
└────────────┴─────────┴────────────┴─────────┴──────────┴─────────────┘

I want to add a value to redemption column for a specific date. The date could be one of the existing dates, or a new one. If it is an existing date, I will just update the existing value. Otherwise, I should create a new row with the new value set and other columns set to null.

In pandas I used to do this using df.loc[date, 'redemption'] = new_value. In polars assigning to a non-existing row raises polars.exceptions.OutOfBoundsError: indices are out of bounds. What is the most idiomatic way to achieve the same in polars?

My current approach:

from polars import *
from datetime import date

df = from_repr("""\
┌────────────┬─────────┬────────────┬─────────┬──────────┬─────────────┐
│ gDate      ┆ pl      ┆ redemption ┆ pc      ┆ ppc      ┆ tval        │
│ ---        ┆ ---     ┆ ---        ┆ ---     ┆ ---      ┆ ---         │
│ date       ┆ f64     ┆ f64        ┆ f64     ┆ f64      ┆ f64         │
╞════════════╪═════════╪════════════╪═════════╪══════════╪═════════════╡
│ 2024-01-30 ┆ 10069.0 ┆ 10068.0    ┆ 10068.0 ┆ 0.07     ┆ 1492.033    │
│ 2024-01-31 ┆ 10082.0 ┆ 10075.0    ┆ 10082.0 ┆ 0.14     ┆ 1236.318    │
│ 2024-02-03 ┆ 10095.0 ┆ 10095.0    ┆ 10095.0 ┆ 0.13     ┆ 2266.253    │
│ 2024-02-04 ┆ 10102.0 ┆ 10102.0    ┆ 10103.0 ┆ 0.08     ┆ 1418.583    │
│ 2024-02-05 ┆ 10110.0 ┆ 10109.0    ┆ 10109.0 ┆ 0.06     ┆ 1940.722    │
│ …          ┆ …       ┆ …          ┆ …       ┆ …        ┆ …           │
│ 2024-03-26 ┆ 10113.0 ┆ 10112.0    ┆ 10112.0 ┆ 0.07     ┆ 1268.171    │
│ 2024-03-27 ┆ 10126.0 ┆ 10119.0    ┆ 10126.0 ┆ 0.14     ┆ 1240.438    │
│ 2024-03-30 ┆ 10149.0 ┆ 10141.0    ┆ 10148.0 ┆ 0.22     ┆ 1317.312    │
│ 2024-04-02 ┆ 10163.0 ┆ 10162.0    ┆ 10162.0 ┆ 0.14     ┆ 1675.402    │
│ 2024-04-03 ┆ 10177.0 ┆ 10169.0    ┆ 10176.0 ┆ 0.137768 ┆ 1546.426394 │
└────────────┴─────────┴────────────┴─────────┴──────────┴─────────────┘""")
date = date(2024, 4, 3)
new_value = 7777

df = df.join(
    DataFrame([[date], [new_value]]),
    left_on='gDate',
    right_on='column_0',
    how='outer_coalesce',
).with_columns(
    when(col('column_1').is_null())
    .then(col('redemption'))
    .otherwise(col('column_1'))
).drop('column_1')

print(df)


Solution

  • .update() is perhaps a little simpler.

    df.update(
       pl.select(gDate = pl.date(2024, 4, 3), redemption = 7777),
       on = "gDate",
       how = "outer"
    )
    
    shape: (10, 6)
    ┌────────────┬─────────┬────────────┬─────────┬──────────┬─────────────┐
    │ gDate      ┆ pl      ┆ redemption ┆ pc      ┆ ppc      ┆ tval        │
    │ ---        ┆ ---     ┆ ---        ┆ ---     ┆ ---      ┆ ---         │
    │ date       ┆ f64     ┆ f64        ┆ f64     ┆ f64      ┆ f64         │
    ╞════════════╪═════════╪════════════╪═════════╪══════════╪═════════════╡
    │ 2024-01-30 ┆ 10069.0 ┆ 10068.0    ┆ 10068.0 ┆ 0.07     ┆ 1492.033    │
    │ 2024-01-31 ┆ 10082.0 ┆ 10075.0    ┆ 10082.0 ┆ 0.14     ┆ 1236.318    │
    │ 2024-02-03 ┆ 10095.0 ┆ 10095.0    ┆ 10095.0 ┆ 0.13     ┆ 2266.253    │
    │ 2024-02-04 ┆ 10102.0 ┆ 10102.0    ┆ 10103.0 ┆ 0.08     ┆ 1418.583    │
    │ 2024-02-05 ┆ 10110.0 ┆ 10109.0    ┆ 10109.0 ┆ 0.06     ┆ 1940.722    │
    │ 2024-03-26 ┆ 10113.0 ┆ 10112.0    ┆ 10112.0 ┆ 0.07     ┆ 1268.171    │
    │ 2024-03-27 ┆ 10126.0 ┆ 10119.0    ┆ 10126.0 ┆ 0.14     ┆ 1240.438    │
    │ 2024-03-30 ┆ 10149.0 ┆ 10141.0    ┆ 10148.0 ┆ 0.22     ┆ 1317.312    │
    │ 2024-04-02 ┆ 10163.0 ┆ 10162.0    ┆ 10162.0 ┆ 0.14     ┆ 1675.402    │
    │ 2024-04-03 ┆ 10177.0 ┆ 7777.0     ┆ 10176.0 ┆ 0.137768 ┆ 1546.426394 │
    └────────────┴─────────┴────────────┴─────────┴──────────┴─────────────┘
    

    how='outer' will update existing rows where the key matches while also adding any new rows contained in the given frame

    df.update(
       pl.select(gDate = pl.date(2028, 4, 3), redemption = 7777),
       on = "gDate",
       how = "outer"
    )
    
    shape: (11, 6)
    ┌────────────┬─────────┬────────────┬─────────┬──────────┬─────────────┐
    │ gDate      ┆ pl      ┆ redemption ┆ pc      ┆ ppc      ┆ tval        │
    │ ---        ┆ ---     ┆ ---        ┆ ---     ┆ ---      ┆ ---         │
    │ date       ┆ f64     ┆ f64        ┆ f64     ┆ f64      ┆ f64         │
    ╞════════════╪═════════╪════════════╪═════════╪══════════╪═════════════╡
    │ 2024-01-30 ┆ 10069.0 ┆ 10068.0    ┆ 10068.0 ┆ 0.07     ┆ 1492.033    │
    │ 2024-01-31 ┆ 10082.0 ┆ 10075.0    ┆ 10082.0 ┆ 0.14     ┆ 1236.318    │
    │ 2024-02-03 ┆ 10095.0 ┆ 10095.0    ┆ 10095.0 ┆ 0.13     ┆ 2266.253    │
    │ 2024-02-04 ┆ 10102.0 ┆ 10102.0    ┆ 10103.0 ┆ 0.08     ┆ 1418.583    │
    │ 2024-02-05 ┆ 10110.0 ┆ 10109.0    ┆ 10109.0 ┆ 0.06     ┆ 1940.722    │
    │ …          ┆ …       ┆ …          ┆ …       ┆ …        ┆ …           │
    │ 2024-03-27 ┆ 10126.0 ┆ 10119.0    ┆ 10126.0 ┆ 0.14     ┆ 1240.438    │
    │ 2024-03-30 ┆ 10149.0 ┆ 10141.0    ┆ 10148.0 ┆ 0.22     ┆ 1317.312    │
    │ 2024-04-02 ┆ 10163.0 ┆ 10162.0    ┆ 10162.0 ┆ 0.14     ┆ 1675.402    │
    │ 2024-04-03 ┆ 10177.0 ┆ 10169.0    ┆ 10176.0 ┆ 0.137768 ┆ 1546.426394 │
    │ 2028-04-03 ┆ null    ┆ 7777.0     ┆ null    ┆ null     ┆ null        │
    └────────────┴─────────┴────────────┴─────────┴──────────┴─────────────┘