繁体   English   中英

按其他数据框列过滤熊猫行

[英]filter pandas rows by other dataframe columns

我有3个dataframes p_id已经按日期和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"])

第二个DataFrame

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看起来像:

第一名:( 持有购买详情)

    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

第三名:( 保留有效期的详细信息)

    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 when_product_expired第一个条目将为:

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)

您本质上想要的是此FIFO库存清单。 以我的经验,大熊猫不是将不同行彼此关联的正确工具。 工作流程应为“拆分应用”组合。 如果您将其拆分,却没有真正找到将其重新拼合的方法,则可能是一个格式错误的问题。 您仍然可以通过groupby来完成很多工作,但这是我不会尝试通过一些巧妙的熊猫技巧来解决的。 即使您使它起作用,也很难维护。

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

我为此整理了一些代码。 您建议的DateFrame在其中。 我遍历所有行,并记账有多少库存。 这在队列q完成,队列q中包含每个项目的元素,并且该元素方便地是purchase_date。

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-07-25之前,有9个项目的p_id 1已购买且9被出售。 没有什么库存可以过期。 我修改了df1,以便购买了11件。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM