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

时间:2018-09-12 11:50:34

标签: python pandas dataframe

我有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如下:

第一名:(持有购买的详细信息)

    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

2nd:(持有销售明细)

    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年7月15日

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

  

截止日期2018年7月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年8月15日

1个数量已过期

因此,新的最终条目将是:

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

这次产品过期是在2018年7月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)

1 个答案:

答案 0 :(得分:1)

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

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

为此,我一起整理了一些代码。您建议的DateFrame在其中。我遍历所有行,并记账有多少库存。这是在队列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'])

一些评论:

  • 我依靠您的保证人

      

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

    未为您的示例数据帧提供此功能。在2018-07-25之前,有9个项目的p_id 1已购买且9被出售。没有什么库存可以过期。我修改了df1,以便购买了11件。

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

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