Python模糊字符串匹配作为相关性表/矩阵

8
我有一个包含x个字符串名称和它们关联ID的文件,基本上是两列数据。我想要的是一个相关表格,格式为x乘以x(对于问题数据,同时将其作为x轴和y轴),但是输出不是相关性,而是使用字符串名称作为输入的fuzzywuzzy库的函数fuzz.ratio(x,y)的输出结果。基本上是针对每个条目运行每个条目。这就是我所想到的东西,只是为了展示我的意图。
import pandas as pd
from fuzzywuzzy import fuzz

df = pd.read_csv('random_data_file.csv')

df = df[['ID','String']]
df['String_Dup'] = df['String'] #creating duplicate of data in question
df = df.set_index('ID')

df = df.groupby('ID')[['String','String_Dup']].apply(fuzz.ratio())

但很明显这种方法现在对我不起作用。 非常感谢您的帮助。 它不一定是pandas,只是我相对更熟悉的环境。

我希望我的问题表述清晰,实际上,任何意见都会受到赞赏,

3个回答

5
使用pandas的crosstab函数,然后对每列使用apply函数来计算模糊度。这比我之前的答案要优雅得多。
import pandas as pd
from fuzzywuzzy import fuzz

# Create sample data frame.
df = pd.DataFrame([(1, 'abracadabra'), (2,'abc'), (3,'cadra'), (4, 'brabra')],
                  columns=['id', 'strings'])
# Create the cartesian product between the strings column with itself.
ct = pd.crosstab(df['strings'], df['strings'])
# Note: for pandas versions <0.22, the two series must have different names.
# In case you observe a "Level XX not found" error, the following may help:
# ct = pd.crosstab(df['strings'].rename(), df['strings'].rename())

# Apply the fuzz (column-wise). Argument col has type pd.Series.
ct = ct.apply(lambda col: [fuzz.ratio(col.name, x) for x in col.index])

# This results in the following:
#       strings      abc  abracadabra  brabra  cadra
#       strings
#       abc          100           43      44     25
#       abracadabra   43          100      71     62
#       brabra        44           71     100     55
#       cadra         25           62      55    100

为了简单起见,我在你的问题中建议省略了groupby操作。如果需要对组应用模糊字符串匹配,只需创建一个单独的函数即可:
def cross_fuzz(df):
    ct = pd.crosstab(df['strings'], df['strings'])
    ct = ct.apply(lambda col: [fuzz.ratio(col.name, x) for x in col.index])
    return ct

df.groupby('id').apply(cross_fuzz)

谢谢!只要我不尝试交叉制表相同的系列,这就有效。即我必须复制“strings”并将其命名为其他名称,否则会出现“level strings未找到错误”。如果没有问题,我很高兴接受这个答案。 - WayOutofDepth
以上代码适用于pandas 0.22.0。但是你说得对,早期版本的pandas(例如0.20.3)需要系列具有不同的名称。我相应地编辑了我的答案。感谢你指出这一点。 - normanius

2
在pandas中,两列之间的笛卡尔积可以使用虚拟变量和pd.merge创建。使用apply应用fuzz操作。最后进行透视操作以提取您想要的格式。为简单起见,我省略了groupby操作,但是当然,您可以通过将代码移动到单独的函数中来将该过程应用于所有组表。以下是可能的样子:
import pandas as pd
from fuzzywuzzy import fuzz

# Create sample data frame.
df = pd.DataFrame([(1, 'abracadabra'), (2,'abc'), (3,'cadra'), (4, 'brabra')],
                  columns=['id', 'strings'])

# Cross product, using a temporary column.
df['_tmp'] = 0
mrg = pd.merge(df, df, on='_tmp', suffixes=['_1','_2'])

# Apply the function between the two strings.
mrg['fuzz'] = mrg.apply(lambda s: fuzz.ratio(s['strings_1'], s['strings_2']), axis=1)

# Reorganize data.
ret = mrg.pivot(index='strings_1', columns='strings_2', values='fuzz')
ret.index.name = None 
ret.columns.name = None

# This results in the following:
#              abc  abracadabra  brabra  cadra
# abc          100           43      44     25
# abracadabra   43          100      71     62
# brabra        44           71     100     55
# cadra         25           62      55    100

比交叉表更加流畅。谢谢。 - Sarang Manjrekar

0
import csv
from fuzzywuzzy import fuzz
import numpy as np  

input_file = csv.DictReader(open('random_data_file.csv')) 
string = []
for row in input_file: #file is appended row by row into a python dictionary
    string.append(row["String"]) #keys for the dict. are the headers



#now you have a list of the string values
length = len(string)
resultMat = np.zeros((length, length)) #zeros 2D matrix, with size X * X

for i in range (length):
    for j in range (length):
        resultMat[i][j] = fuzz.ratio(string[i], string[j])

print resultMat

我在一个 NumPy 的二维矩阵中实现了这个功能。我不太擅长使用 Pandas,但我认为你所做的是添加另一列并将其与字符串列进行比较,也就是说:string[i] 将与 string_dub[i] 匹配,所有结果都将是 100。

希望能对你有所帮助。


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