我有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
之后(首先购买的产品将首先过期)
截止日期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)
答案 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件。
.groupby('p_id').apply(function)
或在df['p_id'].unique()
上循环来将其推广到更多p_id。因此,尽管这不是可扩展的解决方案,但希望它对您有所帮助。好看