【问题标题】:filter pandas rows by other dataframe columns按其他数据框列过滤熊猫行
【发布时间】:2018-09-12 11:50:33
【问题描述】:

我有 3 个dataframes 已经按日期和p_id 排序,没有null 值如下:

第一个数据帧

df1 = pd.DataFrame([['2018-07-05',8.0,1],
                    ['2018-07-15',1.0,1],
                    ['2018-08-05',2.0,1],
                    ['2018-08-05',2.0,2]],
      columns=["purchase_date", "qty", "p_id"])

第二个数据帧

df2 = pd.DataFrame([['2018-07-15',2.0,1],
                    ['2018-08-04',7.0,1],
                    ['2018-08-15',1.0,2]], 
      columns=["sell_date", "qty", "p_id"])

第三个数据帧

df3 = pd.DataFrame([['2018-07-25',1.0,1],
                    ['2018-08-15',1.0,1]],
      columns=["expired_date", "qty", "p_id"])

dataframe 看起来像:

1st:(持有购买详情)

    purchase_date   qty     p_id
0   2018-07-05      8.0     1
1   2018-07-15      1.0     1
2   2018-08-05      2.0     1
3   2018-08-05      2.0     2

第二个:(持有销售详情)

    sell_date   qty    p_id
0   2018-07-15  2.0    1
1   2018-08-04  7.0    1
2   2018-08-15  1.0    2

3rd:(持有到期详情)

    expired_date    qty   p_id
0   2018-07-25      1.0   1
1   2018-08-15      1.0   1

现在我要做的是找到购买过期产品的时间
按照FIFO(先购买的产品先过期)



说明:考虑 id 为 1 的产品

截至日期 2018-07-15

我们有8+1个购买数量和-2个销售数量,即总共有8+1-2个库存数量,-ve表示减量

截至日期 2018-07-25

1 个数量已过期,因此我们新的 when_product_expired dataframe 的第一个条目将是:

purchase_date     expired_date    p_id
2018-07-05        2018-07-25      1


然后为下一个到期条目

截至日期 2018-08-04

7 数量已售罄,因此当前数量为 8+1-2-7 = 0

截至日期 2018-08-05

购买了 2 个数量,因此当前数量为 0+2

截至日期 2018-08-15

1 个数量已过期

所以一个新的和最终的条目将是:

purchase_date     expired_date    p_id
2018-07-05        2018-07-25      1
2018-08-05        2018-08-15      1

这次过期的产品是2018-07-25购买的

其实我有日期时间,所以购买和销售时间永远不会相等(您可以假设),同样在销售和到期之前,总会有一定数量的产品库存,即数据是一致的
并提前谢谢你:-)

更新

现在我在想的是将所有日期字段重命名为相同的字段名称,并用负号附加购买、销售、过期dataframe,但这对我没有帮助

df2.qty = df2.qty*-1
df3.qty=df3.qty*-1
new = pd.concat([df1,df2, df3],sort=False)
      .sort_values(by=["purchase_date"],ascending=True)
      .reset_index(drop=True)

【问题讨论】:

  • 我也愿意使用 Pandas 以外的其他工具,我在一些关系数据库中有这些数据
  • 数量也可以是分数

标签: python pandas dataframe


【解决方案1】:

您本质上想要的是这个 FIFO 库存商品列表。根据我的经验,pandas 不是将不同行相互关联的正确工具。工作流程应该是拆分-应用-组合。如果您将其拆分并且没有真正找到如何将其重新组合在一起的方法,则可能是一个表述错误的问题。您仍然可以使用 groupby 完成很多工作,但这是我不会尝试通过 pandas 中的一些巧妙技巧来解决的问题。即使你让它工作,维护也将是地狱。

我不知道您的问题对性能有多重要(即您的数据框有多大)。如果它只有几 10000 个条目,您可以显式循环遍历 pandas 行(警告:这很慢)并手动构建 fifo 列表。

我为此编写了一些代码。您建议的 DateFrame 在那里。我遍历所有行并记账有多少物品有库存。这是在队列q 中完成的,其中包含每个项目的一个元素,并且该元素通常是购买日期。

import queue

import pandas as pd

from pandas import Series, DataFrame

# modified (see text)
df1 = pd.DataFrame([['2018-07-05',8.0,1],
                    ['2018-07-15',3.0,1],
                    ['2018-08-05',2.0,1],
                    ['2018-08-05',2.0,2]],
      columns=["purchase_date", "qty", "p_id"])

df2 = pd.DataFrame([['2018-07-15',2.0,1],
                    ['2018-08-04',7.0,1],
                    ['2018-08-15',1.0,2]], 
      columns=["sell_date", "qty", "p_id"])

df3 = pd.DataFrame([['2018-07-25',1.0,1],
                    ['2018-08-15',1.0,1]],
      columns=["expired_date", "qty", "p_id"])


df1 = df1.rename(columns={'purchase_date':'date'})

df2 = df2.rename(columns={'sell_date':'date'})

df3 = df3.rename(columns={'expired_date' : 'date'})

df3['qty'] *= -1

df2['qty'] *= -1

df = pd.concat([df1,df2])\
      .sort_values(by=["date"],ascending=True)\
      .reset_index(drop=True)

# Necessary to distinguish between sold and expried items while looping
df['expired'] = False
df3['expired'] = True

df = pd.concat([df,df3])\
      .sort_values(by=["date"],ascending=True)\
      .reset_index(drop=True)

#date  qty  p_id  expired
#7-05  8.0     1    False
#7-15  1.0     1    False
#7-15 -2.0     1    False
#7-25 -1.0     1     True
#8-04 -7.0     1    False
#8-05  2.0     1    False
#8-05  2.0     2    False
#8-15 -1.0     2    False
#8-15 -1.0     1     True

# Iteratively build up when_product_expired
when_product_expired = []

# p_id hardcoded here
p_id = 1

# q contains purchase dates for all individual items 'currently' in stock
q = queue.Queue()

for index, row in df[df['p_id'] == p_id].iterrows():
    # if items are bought, put as many as 'qty' into q
    if row['qty'] > 0:
        for tmp in range(int(round(row['qty']))):
            date = row['date']
            q.put(date)
    # if items are sold or expired, remove as many from q. 
    # if expired additionaly save purchase and expiration date into when_product_expired
    elif row['qty'] < 0:
        for tmp in range(int(round(-row['qty']))):
            purchase_date = q.get()
            if row['expired']:
                print 'item p_id 1 was bought on', purchase_date
                when_product_expired.append([purchase_date, row['date'], p_id])

when_product_expired = DataFrame(when_product_expired, columns=['purchase_date', 'expired_date', 'p_id'])

几点说明:

  • 我相信你的保证

    在销售和到期之前,总会有一定数量的产品库存

    这不是为您的示例 DataFrames 提供的。在 2018 年 7 月 25 日之前,有 9 件 p_id 为 1 的商品已购买,9 件已售出。库存中没有任何可能过期的东西。我修改了df1,所以买了11个。

  • 如果违反此假设,队列将尝试获取不存在的项目。在我的机器上导致无限循环。您可能想要捕获异常。
  • 队列的执行效率最低。如果有很多商品有库存,就会有很多数据翻倍。
  • 您可以通过将所有内容放入函数和.groupby('p_id').apply(function) 或循环df['p_id'].unique() 将其推广到更多p_id

因此,虽然这不是可扩展的解决方案,但我希望它对您有所帮助。好看

【讨论】:

  • 感谢您回答@maow,如果您能建议我使用什么其他工具来实现这一点,我将不胜感激?我在一些相互没有关系的关系数据库中有这些数据
  • 先生的数量也可以是分数
猜你喜欢
  • 2019-12-09
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
  • 2019-04-13
  • 2018-07-13
  • 2018-12-22
  • 2014-03-31
  • 2018-01-18
相关资源
最近更新 更多