Python / Pandas - 字符串比较

时间:2017-06-15 12:59:59

标签: python pandas for-loop levenshtein-distance

我有一个字符串/叙述列表,我需要比较并获得每个字符串之间的距离度量。我编写的当前代码可以工作,但是对于较大的列表,它需要花费时间,因为我使用2 for循环。我用levenshtien距离来测量弦之间的距离。

字符串/叙述列表存储在数据框中。

def edit_distance(s1, s2):
   m=len(s1)+1
   n=len(s2)+1

   tbl = {}
   for i in range(m): tbl[i,0]=i
   for j in range(n): tbl[0,j]=j
   for i in range(1, m):
       for j in range(1, n):
            cost = 0 if s1[i-1] == s2[j-1] else 1
            tbl[i,j] = min(tbl[i, j-1]+1, tbl[i-1, j]+1, tbl[i-1, j-1]+cost)
   return tbl[i,j]

def narrative_feature_extraction(df):
    startTime = time.time()
    leven_matrix = np.zeros((len(df['Narrative']),len(df['Narrative'])))  
    for i in range(len(df['Narrative'])):
        for j in range(len(df['Narrative'])):
            leven_matrix[i][j] = edit_distance(df['Narrative'].iloc[i],df['Narrative'].iloc[j])
    endTime = time.time()
    total = (endTime - startTime)
    print "Feature Extraction (Leven) Runtime:" + str(total)
    return leven_matrix 


X = narrative_feature_extraction(df)

如果列表有n个叙述,则生成的X是nxn矩阵,其中行是叙述,列是叙述的比较。例如,对于距离(i,j),它是叙述i和j之间的距离。

有没有办法优化此代码,以便不需要有这么多的for循环?或者有一种pythonic计算方法吗?

1 个答案:

答案 0 :(得分:1)

很难在没有数据/示例的情况下提供确切的代码,但有一些建议:

  • 使用列表理解,比...在范围内快得多......
  • 根据您的熊猫版本," df [i] [j]"索引可能很慢,而是使用.iloc或.loc(如果你想混合使用.iloc [df.index.get_loc(" itemname"),df.columns.get_loc(&#34) ; itemname")]如果你遇到这个问题,可以正确地将loc转换为iloc。(我认为如果你收到用于写入数据帧切片的警告标志并且很大程度上取决于你的python / pandas的版本,那么它很慢有,但没有经过广泛测试)
  • 更好的是,运行所有计算,然后根据您的使用情况一次性投入数据框
  • 如果您喜欢for循环的pythonic读取,请尽量避免在"范围内使用"至少而是在X [:,0]"中使用"例如。我发现在大多数情况下这会更快,你可以使用枚举来保持索引值(例如下面的内容)

实例/定时:

response = client.post('/models', data=json.dumps(job), headers=headers)

输出:

def test1(): #list comprehension
    X=np.random.normal(size=(100,2))
    results=[[x*y for x in X[:,0]] for y in X[:,1]]
    df=pd.DataFrame(data=np.array(results))

if __name__ == '__main__':
    import timeit
    print("test1: "+str(timeit.timeit("test1()", setup="from __main__ import test1",number=10)))

def test2(): #enumerate, df at end
    X=np.random.normal(size=(100,2))
    results=np.zeros((100,100))
    for ind,i in enumerate(X[:,0]):
        for col,j in enumerate(X[:,1]):
            results[ind,col]=i*j
    df=pd.DataFrame(data=results)

if __name__ == '__main__':
    import timeit
    print("test2: "+str(timeit.timeit("test2()", setup="from __main__ import test2",number=10)))

def test3(): #in range, but df at end
    X=np.random.normal(size=(100,2))
    results=np.zeros((100,100))
    for i in range(len(X)):
        for j in range(len(X)):
            results[i,j]=X[i,0]*X[j,1]
    df=pd.DataFrame(data=results)

if __name__ == '__main__':
    import timeit
    print("test3: "+str(timeit.timeit("test3()", setup="from __main__ import test3",number=10)))

def test4(): #current method
    X=np.random.normal(size=(100,2))
    df=pd.DataFrame(data=np.zeros((100,100)))
    for i in range(len(X)):
        for j in range(len(X)):
            df[i][j]=(X[i,0]*X[j,1])

if __name__ == '__main__':
    import timeit
    print("test4: "+str(timeit.timeit("test4()", setup="from __main__ import test4",number=10)))

因此列表理解速度提高约250倍,枚举速度是"对于范围内的x"速度的两倍。虽然真正的减速是你的数据帧的单独索引(即使使用.loc或.iloc,这仍然是你的瓶颈,所以我建议在可能的情况下使用df之外的数组)

希望这会有所帮助,您可以申请。我建议您阅读地图,过滤,缩小(可能是枚举)功能以及它们非常快速并且可以帮助您:http://book.pythontips.com/en/latest/map_filter.html

不幸的是,我对您的用例并不熟悉,但我不明白为什么它不适用于这种类型的代码调整或与之兼容。