我有一个极坐标数据框,我正在尝试查找一个键上的组之间的多个列上的差异(其值已更改的字段)。数据框中可以有多个组和多个列。这些组本质上是 int 格式的日期时间 (YYYYMMDD)
我如何找到日期上(任何)列有新值的行。
样本数据:
raw_df = pl.DataFrame([
{'id': 'AAPL','update_time': 20241112,'status':'trading', 'underlying': 'y'},
{'id': 'MSFT','update_time': 20241113,'status': 'trading', 'underlying': 'x'},
{'id': 'NVDA','update_time': 20241112,'status': 'trading', 'underlying': 'z'},
{'id': 'MSFT','update_time': 20241112,'status': 'pending','underlying': 'x'},
{'id': 'AAPL','update_time': 20241113,'status': 'trading', 'underlying': 'y'},
{'id': 'NVDA','update_time': 20241113,'status': 'trading', 'underlying': 'z'},
{'id': 'TSLA','update_time': 20241112,'status': 'closed', 'underlying': 'v'},
]
)
expected_df = pl.DataFrame([
{'id': 'MSFT','update_time': 20241112,'status':'pending', 'underlying': 'x'},
{'id': 'MSFT','update_time': 20241113,'status': 'trading', 'underlying': 'x'},
]
)
以下是数据输入的样子。
shape: (7, 4)
┌──────┬─────────────┬─────────┬────────────┐
│ id ┆ update_time ┆ status ┆ underlying │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ str ┆ str │
╞══════╪═════════════╪═════════╪════════════╡
│ AAPL ┆ 20241112 ┆ trading ┆ y │
│ MSFT ┆ 20241113 ┆ trading ┆ x │
│ NVDA ┆ 20241112 ┆ trading ┆ z │
│ MSFT ┆ 20241112 ┆ pending ┆ x │
│ AAPL ┆ 20241113 ┆ trading ┆ y │
│ NVDA ┆ 20241113 ┆ trading ┆ z │
│ TSLA ┆ 20241112 ┆ closed ┆ v │
└──────┴─────────────┴─────────┴────────────┘
下面是预期的结果,显示了 id、更新时间和已更改的字段。如果更改/更新的字段超过 1 个,理想情况下应将其放入新行。
我正在尝试查找已更改的字段,并按键“id”上的“update_time”分组。需要注意的是,一个组中可能存在一些 id,但另一个组中不存在,例如“TSLA”。因此,这些不常见的 id 或组之间的交集可以被忽略。由于只有 MSFT 的状态发生了更改,因此应将其过滤到已更新的两行。字段更改只能在除 update_time 之外的所有其他列上完成,update_time 是我们用来分组的依据。
shape: (2, 3)
┌──────┬─────────────┬─────────┐
│ id ┆ update_time ┆ status │
│ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ str │
╞══════╪═════════════╪═════════╡
│ MSFT ┆ 20241112 ┆ pending │
│ MSFT ┆ 20241113 ┆ trading │
└──────┴─────────────┴─────────┴
无法弄清楚如何做到这一点,但这是我最接近的,它无法解决前面提到的警告。
def find_updated_field_differences(df):
columns_to_check = [col for col in df.columns if col != 'id' and col != 'update_time']
sorted_df = df.sort('update_time')
grouped_df = sorted_df.groupby(["update_time"])
result_data = []
for group_key, group_df in grouped_df:
print(group_df)
for col in columns_to_check:
group_df = group_df.with_columns(
(pl.col(col) != pl.col(col).shift()).alias(f"{col}_changed")
)
differing_rows = group_df.filter(
pl.any([pl.col(f"{col}_changed") for col in columns_to_check])
)
result_data.append(differing_rows)
differing_df = pl.concat(result_data)
differing_df = differing_df.sort("id")
return differing_df
任何帮助将不胜感激!
你的方法有点过于复杂了。我的建议是,你首先按
id
和update_time
对数据进行排序,然后移动数据以准备比较。之后,您可以识别 id
相同但存在差异的行:
import polars as pl
def find_updated_field_differences(df):
sorted_df = df.sort(['id', 'update_time'])
shifted_df = sorted_df.shift(-1)
mask = (
(sorted_df['id'] == shifted_df['id']) &
((sorted_df['status'] != shifted_df['status']) | (sorted_df['underlying'] != shifted_df['underlying']))
)
start_changes = sorted_df.filter(mask)
end_changes = sorted_df.shift(-1).filter(mask)
differing_df = pl.concat([start_changes, end_changes]).unique()
return differing_df.sort(['id', 'update_time'])
raw_df = pl.DataFrame([
{'id': 'AAPL', 'update_time': 20241112, 'status': 'trading', 'underlying': 'y'},
{'id': 'MSFT', 'update_time': 20241113, 'status': 'trading', 'underlying': 'x'},
{'id': 'NVDA', 'update_time': 20241112, 'status': 'trading', 'underlying': 'z'},
{'id': 'MSFT', 'update_time': 20241112, 'status': 'pending', 'underlying': 'x'},
{'id': 'AAPL', 'update_time': 20241113, 'status': 'trading', 'underlying': 'y'},
{'id': 'NVDA', 'update_time': 20241113, 'status': 'trading', 'underlying': 'z'},
{'id': 'TSLA', 'update_time': 20241112, 'status': 'closed', 'underlying': 'v'},
])
result_df = find_updated_field_differences(raw_df)
print(result_df)
这给了你
shape: (2, 4)
┌──────┬─────────────┬─────────┬────────────┐
│ id ┆ update_time ┆ status ┆ underlying │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ str ┆ str │
╞══════╪═════════════╪═════════╪════════════╡
│ MSFT ┆ 20241112 ┆ pending ┆ x │
│ MSFT ┆ 20241113 ┆ trading ┆ x │