在Pandas DataFrame中跨列映射str.contains

8

Python入门 - 我想创建一个字符串映射的字典及其相关值。我有一个数据框,希望创建一个新列,其中如果该字符串匹配,则将该列标记为x。

df = pd.DataFrame({'comp':['dell notebook', 'dell notebook S3', 'dell notepad', 'apple ipad', 'apple ipad2', 'acer chromebook', 'acer chromebookx', 'mac air', 'mac pro', 'lenovo x4'],
              'price':range(10)})

例如,我想采用上面的df并创建一个新列df ['company'],然后将其设置为字符串映射。
我考虑做类似以下的操作:
product_map = {'dell':'Dell Inc.',
               'apple':'Apple Inc.',
               'acer': 'Acer Inc.',
               'mac': 'Apple Inc.',
               'lenovo': 'Dell Inc.'}

接下来我想遍历它,检查df.comp列,并查看每个条目是否包含其中一个字符串,并将df.company列设置为字典中的值。

不确定如何正确执行此操作。


请参考以下链接:https://dev59.com/Zajka4cB1Zd3GeqPBZ7l#48510563 - pault
@pault,我会更新我的示例,因为您的解决方法并不是我想要解决的问题。谢谢您的建议。 - Matt W.
所以,“戴尔笔记本电脑”应该替换为“戴尔公司”或“戴尔公司笔记本电脑”? - Vaishali
不应该被替换,新的列 df['Company'] 应该填充为 Dell Inc.,因为 key 在字符串 dell notebook 中。 - Matt W.
@aquil.abdullah 下面的解决方案难道没有达到你的目标吗? - pault
几乎了解了...我已经成功地将其编辑成我想要的样子。只是在检查它。 - Matt W.
4个回答

10

有许多方法可以做到这一点。其中一种方法是:

def like_function(x):
    group = "unknown"
    for key in product_map:
        if key in x:
            group = product_map[key]
            break
    return group

df['company'] = df.comp.apply(like_function)

这个翻译接近正确,但并不完全准确,因为我需要键,值对中的作为输出结果。 - Matt W.
你可能已经想到了,但我做出了改变,现在返回的是product_map的值而不是键。 - aquil.abdullah

5

如果你正在学习Python,这里有一个有趣的方法。你可以继承dict并重写__getitem__方法来查找部分字符串。

class dict_partial(dict):
    def __getitem__(self, value):
        for k in self.keys():
            if k in value:
                return self.get(k)
        else:
            return self.get(None)

product_map = dict_partial({'dell':'Dell Inc.', 'apple':'Apple Inc.',
                            'acer': 'Acer Inc.', 'mac': 'Apple Inc.',
                            'lenovo': 'Dell Inc.'})

df['company'] = df['comp'].apply(lambda x: product_map[x])

               comp  price     company
# 0     dell notebook      0   Dell Inc.
# 1  dell notebook S3      1   Dell Inc.
# 2      dell notepad      2   Dell Inc.
# 3        apple ipad      3  Apple Inc.
# 4       apple ipad2      4  Apple Inc.
# 5   acer chromebook      5   Acer Inc.
# 6  acer chromebookx      6   Acer Inc.
# 7           mac air      7  Apple Inc.
# 8           mac pro      8  Apple Inc.
# 9         lenovo x4      9   Dell Inc.

我唯一的烦恼是,子类化 dict 时不能同时覆盖 [] 语法和 dict.get 方法。如果可以这样做,我们就可以摆脱 lambda 并使用 df['comp'].map(product_map.get)。目前似乎没有明显的解决方案。


我认为你的dict缺少一个元素 - 'mac': 'Apple Inc.' - pault
1
@pault,我又修复了(我想)。它的美妙之处在于类实例易于创建和重用。只是很遗憾你不能完全使用它,即dict.get不起作用。 - jpp
是的,现在我专门使用Python,我想念C++时的一些东西,其中之一就是函数重载。 - pault

4
据我所知,pandas没有提供“子字符串映射”方法。.map()方法不支持子字符串,而.str.contains()方法只能使用正则表达式(这在规模上不可扩展)。
您可以通过编写一个简单的函数来实现您想要的结果。然后,您可以使用.apply()lambda函数结合使用,生成您所需的“company”列。额外的好处是它使您的代码易读,并且您可以重复使用该功能。希望对您有所帮助。
以下代码将为您提供所需的“company”列:
def map_substring(s, dict_map):
    for key in dict_map.keys():
        if key in s: 
            return dict_map[key]
    return np.nan

df['company'] = df['product'].apply(lambda x: map_substring(x, product_map))

注意 - 在上面的示例中,我假设值只能映射到一个子字符串。 - fpersyn

2

这是由 MaxU 的解决方案启发的矢量化解决方案,用于解决一个类似问题

x = df.comp.str.split(expand=True)
df['company'] = None
df['company'] = df['company'].fillna(x[x.isin(product_map.keys())]\
                                     .ffill(axis=1).bfill(axis=1).iloc[:, 0])
df['company'].replace(product_map, inplace=True)
print(df)
#               comp  price     company
#0     dell notebook      0   Dell Inc.
#1  dell notebook S3      1   Dell Inc.
#2      dell notepad      2   Dell Inc.
#3        apple ipad      3  Apple Inc.
#4       apple ipad2      4  Apple Inc.
#5   acer chromebook      5   Acer Inc.
#6  acer chromebookx      6   Acer Inc.
#7           mac air      7  Apple Inc.
#8           mac pro      8  Apple Inc.
#9         lenovo x4      9   Dell Inc.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接