使用 Pandas 数据帧查找多个索引上所有列之间的差异(增量)

Posted

技术标签:

【中文标题】使用 Pandas 数据帧查找多个索引上所有列之间的差异(增量)【英文标题】:Find difference (delta) between all columns over multiple indexes using Pandas dataframes 【发布时间】:2021-02-05 17:25:30 【问题描述】:

以以下为例:

import datetime
import pandas as pd

data = [
    "date": datetime.date(2020, 1, 1), "product": "product_1", "price": 90, "quantity": 100, "code": "code-1.1",
    "date": datetime.date(2020, 1, 1), "product": "product_2", "price": 80, "quantity": 80, "code": "code-2.1",
    "date": datetime.date(2020, 1, 1), "product": "product_3", "price": 80, "quantity": 80, "code": "code-3.1",

    "date": datetime.date(2020, 1, 2), "product": "product_1", "price": 90, "quantity": 80, "code": "code-1.1",
    "date": datetime.date(2020, 1, 2), "product": "product_2", "price": 80, "quantity": 80, "code": "code-2.1",
    "date": datetime.date(2020, 1, 2), "product": "product_3", "price": 80, "quantity": 80, "code": "code-3.1",

    "date": datetime.date(2020, 1, 3), "product": "product_1", "price": 90, "quantity": 80, "code": "code-1.1",
    "date": datetime.date(2020, 1, 3), "product": "product_2", "price": 80, "quantity": 80, "code": "code-2.2",

    "date": datetime.date(2020, 1, 4), "product": "product_1", "price": 80, "quantity": 70, "code": "code-1.1",
    "date": datetime.date(2020, 1, 4), "product": "product_2", "price": 70, "quantity": 80, "code": "code-2.2",
    "date": datetime.date(2020, 1, 4), "product": "product_3", "price": 80, "quantity": 80, "code": "code-3.1",
]

df = pd.DataFrame(data)
df = df.set_index(["date", "product"])

希望我能够在每个非索引列上产生差异/增量,并且只输出更改的列并且不输出没有任何变化的天数,但还要确定何时错过了天数,然后第二天输出该日期/产品的所有列。

一个示例输出是:

"date": datetime.date(2020, 1, 1), "product": "product_1", "price": 90, "quantity": 100, "code": "code-1.1",
"date": datetime.date(2020, 1, 1), "product": "product_2", "price": 80, "quantity": 80, "code": "code-2.1",
"date": datetime.date(2020, 1, 1), "product": "product_3", "price": 80, "quantity": 80, "code": "code-3.1",
"date": datetime.date(2020, 1, 2), "product": "product_1", "price": None, "quantity": 80, "code": None,
"date": datetime.date(2020, 1, 3), "product": "product_2", "price": None, "quantity": None, "code": "code-2.2",
"date": datetime.date(2020, 1, 4), "product": "product_1", "price": 80, "quantity": 70, "code": None,
"date": datetime.date(2020, 1, 4), "product": "product_2", "price": 70, "quantity": None, "code": None,
"date": datetime.date(2020, 1, 4), "product": "product_3", "price": 80, "quantity": 80, "code": "code-3.1",

澄清以上内容:

对于product_1:日期 2020-01-03 缺失,因为列(价格、数量)中没有任何变化 - 而对于日期 2020-01-02,价格为 None,因为它没有变化。

对于product_2:日期 2020-01-01 由于是第一个数据点而存在,代码在 2020-01-03 更改,价格在 2020-01-04 更新。

对于product_3:日期 2020-01-01 由于是第一个数据点而存在,日期 2020-01-04 由于上一个日期 (2020-01-03) 而存在,我们没有任何数据那天。

我曾尝试对数据框进行迭代,但担心扩展速度会很慢。我觉得像pct_change 这样的东西会起作用,但是从阅读这对多索引来说就不起作用了。

同样,答案也反映了 diff 可以在 int 列上工作,但是我理想情况下也需要这个也可以在 str 列上工作。

【问题讨论】:

您可以使用 df['price']-df['price'].shift(-1) 例如获取从一天到下一天的价格差异,然后使用 nonzero 获取列不为零的索引以进行删除或屏蔽跨度> @G.Anderson 但我想在多个列(价格和数量)上执行此操作,并且将来会在更多列上执行此操作。 【参考方案1】:

对于单个产品

diffs = df.diff()
df[diffs == 0] = None
df[~(diffs == 0).all(axis=1)]
        price   quantity
date    product     
2020-01-01  product_1   90.0    100.0
2020-01-02  product_1   NaN     80.0
2020-01-04  product_1   80.0    70.0

适用于多种产品

def show_diffs(df):
    diffs = df.diff()
    df[diffs == 0] = None
    return df[~(diffs == 0).all(axis=1)]

df.groupby('product').apply(show_diffs).sort_values('date')  
                                    price   quantity
product     date        product     
product_1   2020-01-01  product_1   90.0    100.0
product_2   2020-01-01  product_2   80.0    80.0
product_1   2020-01-02  product_1   NaN     80.0
            2020-01-04  product_1   80.0    70.0
product_2   2020-01-04  product_2   70.0    NaN

【讨论】:

如果只有一个产品,这可以工作,如果有多个产品,这将如何工作? 这取决于您的数据框如何存储产品信息。如果您在同一列中有“product_1”、“product_2”等,那么您可能可以使用groupby 构造它们并将上述步骤作为函数传递。 @MarcusChaplais 添加了 groupby 解决方案 看起来不错!如果我添加一个只处理字符串的列,这将如何工作?例如product_code,它可能会随着时间的推移而改变,我也想有一个差异?据我了解.diff() 只处理数字。 也许从它创建int 列并使用它? df['product_code'] = df.index.get_level_values('product').str[-1].astype(int).

以上是关于使用 Pandas 数据帧查找多个索引上所有列之间的差异(增量)的主要内容,如果未能解决你的问题,请参考以下文章

Pandas 将多个数据帧与存储在多个列上的查找值合并

查找多个数据框列之间的公共元素

Pandas:使用循环和分层索引将多个 csv 文件导入数据帧

使用 pandas 或 numpy 从一个 csv 加载多个数据帧

将具有相同列/索引的两个 pandas DataFrame 合并为一个 DataFrame

Pandas 将多个数据帧与时间戳索引对齐