Pandas 合并、缩放和旋转长格式和宽格式数据帧
Pandas merge, scale, and pivot long-form and wide-form dataframes
我有两个 Pandas 数据框需要合并。第一个是长格式数据集,其中包含我在不同数量间断的商品售价。价格随着购买零件数量的增加而下降。
Dataframe1
PART# MY_QTY MY_PRC
Item1 1
Item1 10
Item1 20
Item2 1 0
Item2 30 0
Item2 50
第二个是宽格式数据集,其中包含多个供应商的数量细分和售价。对于下面的 Item1,如果我从 Vend1 购买 1 件,我支付 10 美元,4 件仍然是 10 美元,5 件是 8 美元,等等。数量中断的数量因项目和供应商而异,并非所有供应商都出售所有项目。
Dataframe2
PART# VEND# QTY1 PRC1 QTY2 PRC2 QTY3 PRC3
Item1 Vend1 1 5 15
Item1 Vend2 1 11 30
Item1 Vend3 1 10
Item2 Vend1 1 20 30
Item2 Vend2 1 12
我想合并数据框,以便我可以将每次数量中断时的售价与相同数量时的供应商成本进行比较。最终的数据框将在 PART# 上具有左合并的形状,VEND# 的旋转到列。
我遇到困难的部分是根据 MY_QTY 获取正确的供应商价格。我应该能够跨行阅读并查看所有各方对给定数量的商品收取的费用。预期输出如下。
结果数据框
PART# MY_QTY MY_PRC VEND1 VEND2 VEND3
Item1 1
Item1 10
Item1 20
Item2 1 0
Item2 30 0
Item2 50
编辑
人们似乎对 Dataframe2 感到困惑。此数据帧按行读取。第一行值显示 Vend1 销售的 Item1 的价格。对于这一行,从 QTY1(1 件)到 QTY2(5 件)的价格为 PRC1(10 美元),然后从 QTY2(5 件)到 QTY3(15 件)的价格为 PRC2(8 美元)。价格保持不变,直到请求的数量达到下一个数量突破点。
假设 Mama's Farm Stand 以每个 1 美元的价格出售苹果。如果您购买 5 个苹果,那么每个苹果的价格会下降到 0.75 美元。如果您购买 15 个苹果,那么价格会再次下降到 0.50 美元。此示例的数据框如下所示。
PART# VEND# QTY1 PRC1 QTY2 PRC2 QTY3 PRC3
Apple Mama 1 5 $.75 15 $.5
dfs = []
for val in ['PRC1','PRC2','PRC3']:
temp = pd.pivot_table(df2, index='PART#', columns='VEND#', values=val).reset_index()
dfs.append(temp)
pivot = pd.concat(dfs, axis=0)
pivot.sort_values('PART#',inplace=True)
pivot.reset_index(inplace=True)
df1.join(pivot,lsuffix='PART#')
这里有一个工作示例,说明您可以如何操作。这绝不是有效的。其他人似乎试图加入这两个数据集,但听起来您想要的实际上是为每个供应商/零件组合获得最大 QTY <= MY_QTY
的价格。
import pandas as pd
from io import StringIO
import numpy as np
df1_t = StringIO("""PART#,MY_QTY,MY_PRC
Item1,1,
Item1,10,
Item1,20,
Item2,1,0
Item2,30,0
Item2,50,
""")
df2_t = StringIO("""PART#,VEND#,QTY1,PRC1,QTY2,PRC2,QTY3,PRC3
Item1,Vend1,1,,5,,15,
Item1,Vend2,1,,11,,30,
Item1,Vend3,1,,10,
Item2,Vend1,1,,20,,30,
Item2,Vend2,1,,12,
""")
df1 = pd.read_csv(df1_t)
df2 = pd.read_csv(df2_t)
vendors = df2['VEND#'].unique()
items = df2['PART#'].unique()
# for the specific item and vendor in the rows of Dataframe1 (df1), find the
# largest QTY for that that's less than MY_QTY for the same combination of item
# and vendor in df2
def find_price(row, vendor, df2):
item = row['PART#']
quantity = row['MY_QTY']
# get the row with that specific item / vendor combo
prices = df2[(df2['PART#']==item) & (df2['VEND#']==vendor)]
# reshape a little
prices = pd.wide_to_long(prices, ['QTY','PRC'], i='VEND#', j='v').set_index('QTY',append=True).reset_index().drop('v',axis=1)
# only get where QTY <= MY_QTY
prices = prices[prices['QTY']<=quantity]
if prices.empty:
return np.nan
else:
return prices.loc[prices['QTY'].argmax(),:]['PRC']
# iterate throw the vendors, and use find_price to get the corresponding price
for vendor in vendors:
df1[vendor] = df1.apply(lambda row: find_price(row, vendor, df2),axis=1)
print(df1)
# PART# MY_QTY MY_PRC Vend1 Vend2 Vend3
#0 Item1 1
#1 Item1 10
#2 Item1 20
#3 Item2 1 0 NaN
#4 Item2 30 0 NaN
#5 Item2 50 NaN
这是另一种只对供应商使用循环的方法,但需要对数据进行排序
import pandas as pd
from io import StringIO
import numpy as np
df1_t = StringIO("""PART#,MY_QTY,MY_PRC
Item1,1,
Item1,10,
Item1,20,
Item2,1,0
Item2,30,0
Item2,50,
""")
df2_t = StringIO("""PART#,VEND#,QTY1,PRC1,QTY2,PRC2,QTY3,PRC3
Item1,Vend1,1,,5,,15,
Item1,Vend2,1,,11,,30,
Item1,Vend3,1,,10,
Item2,Vend1,1,,20,,30,
Item2,Vend2,1,,12,
""")
df1 = pd.read_csv(df1_t)
df2 = pd.read_csv(df2_t)
df2 = pd.wide_to_long(df2, ['QTY','PRC'], i='VEND#', j='v').set_index('QTY',append=True).reset_index().drop('v',
axis=1)
df1['MY_QTY'] = df1['MY_QTY'].astype(float)
df1 = df1.sort_values(by="MY_QTY")
df2 = df2.sort_values(by="QTY")
df2 = df2.dropna(axis=0, how='any')
vendors = df2['VEND#'].unique()
df3=df1
for vendor in vendors:
df3 = pd.merge_asof(df3, df2[df2['VEND#']==vendor], left_on="MY_QTY", right_on="QTY", by='PART#',suffixes=('', '_y'))
to_drop = [x for x in df3 if x.startswith('VEND')]
to_drop = to_drop + [x for x in df3 if x.startswith('QTY')]
df3.drop(to_drop, axis=1, inplace=True)
df3 = df3.rename(columns={prc : vendor for prc, vendor in zip([x for x in df3 if x.startswith('PRC')], vendors)})
print(df3)
# PART# MY_QTY MY_PRC Vend1 Vend3 Vend3
#0 Item1 1.0
#1 Item2 1.0 0 NaN
#2 Item1 10.0
#3 Item1 20.0
#4 Item2 30.0 0 NaN
#5 Item2 50.0 NaN
我有两个 Pandas 数据框需要合并。第一个是长格式数据集,其中包含我在不同数量间断的商品售价。价格随着购买零件数量的增加而下降。
Dataframe1
PART# MY_QTY MY_PRC
Item1 1
Item1 10
Item1 20
Item2 1 0
Item2 30 0
Item2 50
第二个是宽格式数据集,其中包含多个供应商的数量细分和售价。对于下面的 Item1,如果我从 Vend1 购买 1 件,我支付 10 美元,4 件仍然是 10 美元,5 件是 8 美元,等等。数量中断的数量因项目和供应商而异,并非所有供应商都出售所有项目。
Dataframe2
PART# VEND# QTY1 PRC1 QTY2 PRC2 QTY3 PRC3
Item1 Vend1 1 5 15
Item1 Vend2 1 11 30
Item1 Vend3 1 10
Item2 Vend1 1 20 30
Item2 Vend2 1 12
我想合并数据框,以便我可以将每次数量中断时的售价与相同数量时的供应商成本进行比较。最终的数据框将在 PART# 上具有左合并的形状,VEND# 的旋转到列。
我遇到困难的部分是根据 MY_QTY 获取正确的供应商价格。我应该能够跨行阅读并查看所有各方对给定数量的商品收取的费用。预期输出如下。
结果数据框
PART# MY_QTY MY_PRC VEND1 VEND2 VEND3
Item1 1
Item1 10
Item1 20
Item2 1 0
Item2 30 0
Item2 50
编辑
人们似乎对 Dataframe2 感到困惑。此数据帧按行读取。第一行值显示 Vend1 销售的 Item1 的价格。对于这一行,从 QTY1(1 件)到 QTY2(5 件)的价格为 PRC1(10 美元),然后从 QTY2(5 件)到 QTY3(15 件)的价格为 PRC2(8 美元)。价格保持不变,直到请求的数量达到下一个数量突破点。
假设 Mama's Farm Stand 以每个 1 美元的价格出售苹果。如果您购买 5 个苹果,那么每个苹果的价格会下降到 0.75 美元。如果您购买 15 个苹果,那么价格会再次下降到 0.50 美元。此示例的数据框如下所示。
PART# VEND# QTY1 PRC1 QTY2 PRC2 QTY3 PRC3
Apple Mama 1 5 $.75 15 $.5
dfs = []
for val in ['PRC1','PRC2','PRC3']:
temp = pd.pivot_table(df2, index='PART#', columns='VEND#', values=val).reset_index()
dfs.append(temp)
pivot = pd.concat(dfs, axis=0)
pivot.sort_values('PART#',inplace=True)
pivot.reset_index(inplace=True)
df1.join(pivot,lsuffix='PART#')
这里有一个工作示例,说明您可以如何操作。这绝不是有效的。其他人似乎试图加入这两个数据集,但听起来您想要的实际上是为每个供应商/零件组合获得最大 QTY <= MY_QTY
的价格。
import pandas as pd
from io import StringIO
import numpy as np
df1_t = StringIO("""PART#,MY_QTY,MY_PRC
Item1,1,
Item1,10,
Item1,20,
Item2,1,0
Item2,30,0
Item2,50,
""")
df2_t = StringIO("""PART#,VEND#,QTY1,PRC1,QTY2,PRC2,QTY3,PRC3
Item1,Vend1,1,,5,,15,
Item1,Vend2,1,,11,,30,
Item1,Vend3,1,,10,
Item2,Vend1,1,,20,,30,
Item2,Vend2,1,,12,
""")
df1 = pd.read_csv(df1_t)
df2 = pd.read_csv(df2_t)
vendors = df2['VEND#'].unique()
items = df2['PART#'].unique()
# for the specific item and vendor in the rows of Dataframe1 (df1), find the
# largest QTY for that that's less than MY_QTY for the same combination of item
# and vendor in df2
def find_price(row, vendor, df2):
item = row['PART#']
quantity = row['MY_QTY']
# get the row with that specific item / vendor combo
prices = df2[(df2['PART#']==item) & (df2['VEND#']==vendor)]
# reshape a little
prices = pd.wide_to_long(prices, ['QTY','PRC'], i='VEND#', j='v').set_index('QTY',append=True).reset_index().drop('v',axis=1)
# only get where QTY <= MY_QTY
prices = prices[prices['QTY']<=quantity]
if prices.empty:
return np.nan
else:
return prices.loc[prices['QTY'].argmax(),:]['PRC']
# iterate throw the vendors, and use find_price to get the corresponding price
for vendor in vendors:
df1[vendor] = df1.apply(lambda row: find_price(row, vendor, df2),axis=1)
print(df1)
# PART# MY_QTY MY_PRC Vend1 Vend2 Vend3
#0 Item1 1
#1 Item1 10
#2 Item1 20
#3 Item2 1 0 NaN
#4 Item2 30 0 NaN
#5 Item2 50 NaN
这是另一种只对供应商使用循环的方法,但需要对数据进行排序
import pandas as pd
from io import StringIO
import numpy as np
df1_t = StringIO("""PART#,MY_QTY,MY_PRC
Item1,1,
Item1,10,
Item1,20,
Item2,1,0
Item2,30,0
Item2,50,
""")
df2_t = StringIO("""PART#,VEND#,QTY1,PRC1,QTY2,PRC2,QTY3,PRC3
Item1,Vend1,1,,5,,15,
Item1,Vend2,1,,11,,30,
Item1,Vend3,1,,10,
Item2,Vend1,1,,20,,30,
Item2,Vend2,1,,12,
""")
df1 = pd.read_csv(df1_t)
df2 = pd.read_csv(df2_t)
df2 = pd.wide_to_long(df2, ['QTY','PRC'], i='VEND#', j='v').set_index('QTY',append=True).reset_index().drop('v',
axis=1)
df1['MY_QTY'] = df1['MY_QTY'].astype(float)
df1 = df1.sort_values(by="MY_QTY")
df2 = df2.sort_values(by="QTY")
df2 = df2.dropna(axis=0, how='any')
vendors = df2['VEND#'].unique()
df3=df1
for vendor in vendors:
df3 = pd.merge_asof(df3, df2[df2['VEND#']==vendor], left_on="MY_QTY", right_on="QTY", by='PART#',suffixes=('', '_y'))
to_drop = [x for x in df3 if x.startswith('VEND')]
to_drop = to_drop + [x for x in df3 if x.startswith('QTY')]
df3.drop(to_drop, axis=1, inplace=True)
df3 = df3.rename(columns={prc : vendor for prc, vendor in zip([x for x in df3 if x.startswith('PRC')], vendors)})
print(df3)
# PART# MY_QTY MY_PRC Vend1 Vend3 Vend3
#0 Item1 1.0
#1 Item2 1.0 0 NaN
#2 Item1 10.0
#3 Item1 20.0
#4 Item2 30.0 0 NaN
#5 Item2 50.0 NaN