【问题标题】:python - search and count bigrams from string (count substring occurence in string)?python - 从字符串中搜索和计算二元组(计算字符串中出现的子串)?
【发布时间】:2017-08-05 14:58:52
【问题描述】:

目标是获取字符串中出现的二元组计数
换句话说,如何获取较大字符串中子字符串的计数?

# Sample data with text
hi = {1: "My name is Lance John", 
  2: "Am working at Savings Limited in Germany",
  3: "Have invested in mutual funds",
  4: "Savings Limited accepts mutual funds as investment option",
  5: "Savings Limited also accepts other investment option"}

hi = pd.DataFrame(hi.items(), columns = ['id', 'notes'])
# have two categories with pre-defined words
name = ['Lance John', 'Germany']
finance = ['Savings Limited', 'investment option', 'mutual funds']

# want count of bigrams in each category for each record
# the output should look like this

ID name finance  
1    1    0  
2    1    2
3    0    1
4    0    3
5    0    2

【问题讨论】:

  • 我知道 string.count(substring),但不知道为每行搜索多个单词的最佳方法?
  • Regex 在这种情况下是最佳的。

标签: python string text-mining n-gram


【解决方案1】:

可以用正则表达式来完成。我们经常假设正则表达式是“神奇的”,因为它们可以在一个函数调用中完成所有事情。

我不知道在不同组中查找不同单词的正则表达式是否比手动搜索更有效 - 但它肯定会比纯 Python 代码中的手动搜索更有效,因为搜索需要放置在一个高度优化的字节码中,在一个紧密的循环中运行。

因此,如果您只有一个组,您所需要的只是一个正则表达式,其中您的模式由“或”(|) 正则表达式运算符分隔 - 它会匹配每个单词。您可以让他们使用“finditer”正则表达式方法以及collections.Counter 数据结构来总结每个单词的出现次数:

In [56]: test = "parrot parrot bicycle parrot inquisition bicycle parrot"

In [57]: expression = re.compile("parrot|bicycle|inquisition")

In [58]: Counter(match.group() for match in expression.finditer(test))
Out[58]: Counter({'parrot': 4, 'bicycle': 2, 'inquisition': 1})

现在,您扩展了这个概念 - 将相关表达式放在名为组的正则表达式中(子模式用括号括起来,括号内以 ?P<groupname> 为前缀,组名用文字括起来 < >)。每个组体是你上面单词的顺序,每个组命名你的集合名称 - 所以:

 expression = r'(?P<finance>Savings\ Limited|investment\ option|mutual\ funds)|(?P<name>Lance\ John|Germany)')

将根据您给出的示例产生名为 financename 的组中的匹配项。要将其与计数器相结合,我们必须使用表达式匹配对象的groupdict 方法,并获取结果字典的键 -

In[65]: Counter(m.groupdict().keys()[0] for m in expression.finditer(hi[1]))
Out[65]: Counter({'finance': 1})

现在只需找到一种以编程方式构建表达式的方法,而不必对其进行硬编码 - 它可以通过两个嵌套的“连接”运算符来完成 - 外部用于连接组,内部用于连接每个组中的术语组。

如果您将术语放入字典中,而不是将每个术语命名为一个孤立的变量,这将更加优雅 - 这样您就可以:

 domains = {'finance': [...], 'names': [...]} 

上面的正则表达式可以通过以下方式构建:

groups = []
for groupname in domains.keys():
    term_group = "|".join(re.escape(term) for term in terms)
    groups.append(r"(?P<{}>{})".format(groupname, term_group)  ) 
expression = re.compile("|".join(groups))

然后,把你的数据晒起来:

data = []
for key, textline in hi.items():
    data.append((key, Counter(m.groupdict().keys()[0] for m in expression.finditer(textline)) ))

(顺便说一句,尝试使用嵌套生成器表达式构建正则表达式是多么难以理解):

 expression = re.compile("|".join("(?P<{0}>{1})".format(
      groupname,
      "|".join(
          "{}".format(
                  re.escape(term)) for term in domains[groupname]
           )
       ) for group in domains.keys() )
 )

【讨论】:

    【解决方案2】:
    hi = {1: "My name is Lance John. Lance John is senior marketing analyst", 
          2: "Am working at Savings Limited in Germany",
          3: "Have invested in mutual funds",
          4: "Savings Limited accepts mutual funds as investment option",
          5: "Savings Limited also accepts other investment option"}
    
    hi = pd.DataFrame(hi.items(), columns = ['id', 'notes'])
    name = ['Lance John', 'Germany', 'senior', 'working']
    finance = ['Savings Limited', 'investment option', 'mutual funds']
    
    def f(cell_value):
        return [((v[1])) for v in ((s, cell_value.count(s)) for s in search) if v]
    
    search = name
    df=hi['notes'].apply(f)
    
    
    search = finance
    df1=hi['notes'].apply(f)
    
    df2 = pd.DataFrame({'name': df.apply(np.count_nonzero), 'finance': df1.apply(np.count_nonzero), 'text': hi['notes']})
    

    能够使用此链接解决它Counting appearances of multiple substrings in a cell pandas
    只是修改了代码以使用 count_nonzero 而不是直接求和来计算唯一出现次数

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-21
      • 2012-02-12
      • 2021-01-24
      • 2023-03-28
      • 1970-01-01
      相关资源
      最近更新 更多