【问题标题】:How to map two dataframes based on splitted items in the columns?如何根据列中的拆分项映射两个数据框?
【发布时间】:2021-10-04 12:40:42
【问题描述】:

我有两个数据框如下:

data = {
    'Name': ['Drama', 'Tennis Elbow', 'Cricket & bat', 'Ant and Boat'],
    'Items': ['abc, def, kgf, do work', 'ball, jig, file code, sensor dye, gun', 'jack and jill, common, bitter', 
             'ram, krish, myran']
}
df1 = pd.DataFrame(data)

df1

    Name            Items
0   Drama           abc, def, kgf, do work
1   Tennis Elbow    ball, jig, file code, sensor dye, gun
2   Cricket & bat   jack and jill, common, bitter
3   Ant and Boat    ram, krish, myran

data2 = {
    'values': ['abc and sea', 'def work', 'abc', 'ram cold', 'myran add', 'check'],
    'gems': ['A1, A2, A3, A4', 'B1, A1, B2, B3', 'C1, A1', 'KS, KM', 'JP, CVK', 'KF, GF']  
}
df2 = pd.DataFrame(data2)

df2

    values        gems
0   abc and sea   A1, A2, A3, A4
1   def work      B1, A1, B2, B3
2   abc           C1, A1
3   ram cold      KS, KM
4   myran add     JP, CVK
5   check         KF, GF

我想将字符串或包含项目的字符串从df1['Items'] 映射到df2['values'],并在新列中创建一个具有映射值的新数据框,如下所示:

    values        gems              Name
0   abc and sea   A1, A2, A3, A4    Drama
1   def work      B1, A1, B2, B3    Drama
2   abc           C1, A1            Drama
3   ram cold      KS, KM            Ant and Boat
4   myran add     JP, CVK           Ant and Boat

【问题讨论】:

  • 如果一个值出现在多个项目中,输出应该是什么?例如,“abc 和球”?或者这永远不会发生?
  • @not_speshal 永远不会发生
  • 为什么def work 匹配do work
  • @HenryEcker 部分字符串 def work 属于 Drama
  • @not_speshal 以逗号分隔。

标签: python pandas


【解决方案1】:

执行此操作的一种方法是从 df1 创建一个映射字典,并使用它来映射来自 df2 的值。

  1. split 单词 df1["Items"] 和 explode 为每个单词创建映射器的列表结果列:
df1["Items"] = df1["Items"].str.split(", ")
mapper = df1.explode("Items")
mapper = dict(zip(mapper["Items"], mapper["Name"]))
  1. 使用映射器获取 df2["values"] 中单词的名称。
df2["Name"] = df2["values"].apply(lambda x: " ".join([mapper.get(word,"") for word in x.split()]).strip())
df2 = df2[df2["Name"]!=""]

输出:

>>>> df2
        values            gems          Name
0  abc and sea  A1, A2, A3, A4         Drama
1     def work  B1, A1, B2, B3         Drama
2          abc          C1, A1         Drama
3     ram cold          KS, KM  Ant and Boat
4    myran add         JP, CVK  Ant and Boat           

【讨论】:

    【解决方案2】:

    首先用逗号分割Items 列,去掉所有剩余的空格,然后分解并重置索引

    >>> df1['Items'] = df1['Items'].str.split(',').apply(lambda x:[i.strip() for i in x])
    >>> df1 = df1.explode('Items').reset_index(drop=True)
    

    然后编写一个函数,根据x 是否包含Items 列中的任何值的条件,返回NameNaN 提供的值x,如果是,则返回第一个值,否则返回NaN

    >>> def getName(x):
            return next(iter(df1.loc[df1['Items'].apply(lambda item: item in x)]['Name']),
                        np.nan)
    

    最后,在第二个数据框的values 列上应用函数getName,将其分配给新列Name,并删除NameNaN 的行。

    >>> df2.assign(Name=df2['values'].apply(getName)).dropna(subset=['Name'])
    
            values            gems          Name
    0  abc and sea  A1, A2, A3, A4         Drama
    1     def work  B1, A1, B2, B3         Drama
    2          abc          C1, A1         Drama
    3     ram cold          KS, KM  Ant and Boat
    4    myran add         JP, CVK  Ant and Boat
    

    【讨论】:

    • 在我的真实数据中有一个短语映射:'异常心脏形态 MP:0000266' 这并不是真正需要的。由于映射列表中的hear 字,它被映射。我只想在映射 heart 而不是 hear 时映射术语
    • 这里怎么申请case_sensitive=False
    • 对于case_insensitive,您可以做的一种方法是将两个值转换为较低的字符串并进行比较,即lambdalambda函数中的item.lower() in x.lower()getName
    • 为了获得更好的性能,您可以将df1Items列中的所有值预先转换为小写,即在爆炸和重置索引后df1['Items'] = df1['Items'].str.lower(),然后在内部tem in x.lower() lambdagetName 函数。
    猜你喜欢
    • 2021-12-07
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-16
    • 2020-01-08
    • 2019-01-11
    相关资源
    最近更新 更多