根据几个条件将列从一个数据帧映射到另一个数据帧,以考虑存在的多个映射中的一个映射
Posted
技术标签:
【中文标题】根据几个条件将列从一个数据帧映射到另一个数据帧,以考虑存在的多个映射中的一个映射【英文标题】:Mapping columns from one dataframe to another based on few conditions to consider one mapping out of multiple mappings present 【发布时间】:2021-09-29 18:13:59 【问题描述】:我有两个数据框 A 和 B 有一个公共列“标签”。我想在数据框 A 中创建一个新列“地图”,其中包含来自数据框 B 的相应映射。
必要条件:
-
每次映射时,将变量计数加 1(将与数据帧 B 中的“容量”列进行比较)
'label' 列的映射应基于数据帧 B 中 'Num' 列的较高值来完成。此外,如果计数变得大于 'Capacity' 以进行下一次分配,则分配次佳的 'Num' 映射,依此类推.
如果没有可用映射或可用映射的“容量”已满,则将“映射”更新为无
数据框 A
Id label
1 1
2 1
3 1
4 2
5 2
6 3
7 3
数据框 B
label Capacity Map Num
1 1 A 0.1
1 2 B 0.2
2 2 C 0.3
3 1 D 0.2
预期的输出数据帧
Id label Map
1 1 B
2 1 B
3 1 A
4 2 C
5 2 C
6 3 D
7 3 None
任何pythonic方式。我希望能对代码进行一些解释。
【问题讨论】:
有任何答案解决了您的问题吗? 【参考方案1】:假设您的初始数据帧是:
>>> dfa
Id label
0 1 1
1 2 1
2 3 1
3 4 2
4 5 2
5 6 3
6 7 3
>>> dfb
label Capacity Map Num
0 1 1 A 0.1
1 1 2 B 0.2
2 2 2 C 0.3
3 3 1 D 0.2
首先,从重构一点数据帧开始。我们计算 dfa 的 cumcount
和 dfb 的 cumsum
。这为我们提供了可以按地图顺序分配多少行,并具有累积限制。
dfa['count'] = dfa.groupby('label').cumcount()+1
dfb.sort_values(by='Num', ascending=False, inplace=True)
dfb['count'] = dfb.groupby('label')['Capacity'].cumsum()
然后我们定义一个自定义函数来进行映射。 try/except 块处理没有行可用于映射的情况,函数将返回None
def custom_map(s):
try:
return (dfb[dfb['label'].eq(s['label']) & # same label
dfb['count'].ge(s['count']) # within capacity
].iloc[0]['Map']) # take first element
except IndexError:
pass
最后,我们使用以下方法映射值:
dfa['Map'] = dfa.apply(custom_map, axis=1)
dfa.drop('count', axis=1)
输出:
Id label Map
0 1 1 B
1 2 1 B
2 3 1 A
3 4 2 C
4 5 2 C
5 6 3 D
6 7 3 None
【讨论】:
【参考方案2】:我试图复制提到的数据框。我的方法是首先按“Num”对“B”数据帧进行排序,然后按“容量”排序。然后循环“A”数据框,我能够选择正确的“地图”标签并减少可用容量。
import pandas as pd
dfA = pd.DataFrame()
dfA["Id"] = [1,2,3,4,5,6,7
]
dfA["label"] = [1,1,1,2,2,3,3]
dfB = pd.DataFrame()
dfB["label"] = [1,1,2,3]
dfB["cap"] = [1,2,2,1]
dfB["map"] = ["A","B","C","D"]
dfB["num"] = [0.1,0.2,0.3,0.2]
test = dfB.copy()
test = test.sort_values(by = ['num', "cap"], ascending = [False, False], na_position = 'first')
map_list = []
for index, row in dfA.iterrows():
currLabel = row["label"]
x = test.loc[test['label'] == currLabel]
if len(x):
foundMap = False
for i,r in x.iterrows():
if r["cap"] > 0:
test.at[i,"cap"] = r["cap"]-1
map_list.append(r["map"])
foundMap = True
break
if not foundMap:
map_list.append(None)
else:
map_list.append(None)
dfA["map"] = map_list
您也可以在 dfB 中创建一个新列,而不是创建 dfB 的副本,这将保持实时容量。
【讨论】:
以上是关于根据几个条件将列从一个数据帧映射到另一个数据帧,以考虑存在的多个映射中的一个映射的主要内容,如果未能解决你的问题,请参考以下文章
将列从一个数据帧合并到另一个数据帧(left_join不起作用) - rstudio