字符串与 Python + Sqlite 的相似度(Levenshtein 距离/编辑距离)

Bas*_*asj 5 python sqlite similarity string-comparison

Python+Sqlite 中是否有可用的字符串相似性度量,例如sqlite3模块?

用例示例:

import sqlite3
conn = sqlite3.connect(':memory:')
c = conn.cursor()
c.execute('CREATE TABLE mytable (id integer, description text)')
c.execute('INSERT INTO mytable VALUES (1, "hello world, guys")')
c.execute('INSERT INTO mytable VALUES (2, "hello there everybody")')
Run Code Online (Sandbox Code Playgroud)

此查询应匹配 ID 为 1 的行,但不匹配 ID 为 2 的行:

c.execute('SELECT * FROM mytable WHERE dist(description, "He lo wrold gyus") < 6')
Run Code Online (Sandbox Code Playgroud)

如何在 Sqlite+Python 中做到这一点?

关于我到目前为止发现的内容的注释:

  • Levenshtein距离,即单字符编辑(插入,删除或替换)的最小数量需要改变一个字到另一个,可能是有用的,但我不知道是否SQLite中存在的正式实施(我看到了一些自定义实现,比如这个

  • 所述Damerau-的Levenshtein是相同的,除了它也允许两个相邻字符之间换位; 它也被称为编辑距离

  • 我知道自己定义一个函数是可能的,但是实现这样的距离并不容易(对数据库进行超高效的自然语言处理比较真的很重要),这就是为什么我想看看 Python / Sqlite 是否已经具有这样的功能一个工具

  • Sqlite 具有 FTS(全文搜索)功能:FTS3FTS4FTS5

    CREATE VIRTUAL TABLE enrondata1 USING fts3(content TEXT);     /* FTS3 table */
    CREATE TABLE enrondata2(content TEXT);                        /* Ordinary table */
    SELECT count(*) FROM enrondata1 WHERE content MATCH 'linux';  /* 0.03 seconds */
    SELECT count(*) FROM enrondata2 WHERE content LIKE '%linux%'; /* 22.5 seconds */
    
    Run Code Online (Sandbox Code Playgroud)

    但我没有找到关于字符串比较与这种“相似距离”、FTS 的特征MATCHNEAR似乎没有字母变化的相似性度量等。

  • 此外,这个答案表明:

    SQLite 的 FTS 引擎基于令牌——搜索引擎试图匹配的关键字。
    有多种标记器可用,但它们相对简单。“简单”分词器简单地拆分每个单词并将其小写:例如,在字符串“The quick brown fox jumps over the lazy dog”中,单词“jumps”会匹配,但不会匹配“jump”。“porter”标记器更高级一些,剥离了单词的共轭关系,因此“jumps”和“jumping”会匹配,但像“jmups”这样的错字不会匹配

    遗憾的是,后者(无法发现“jmups”与“jumps”类似)使得它对我的用例不切实际。

Bas*_*asj 7

这是一个现成的示例test.py

import sqlite3
db = sqlite3.connect(':memory:')
db.enable_load_extension(True)
db.load_extension('./spellfix')                 # for Linux
#db.load_extension('./spellfix.dll')            # <-- UNCOMMENT HERE FOR WINDOWS
db.enable_load_extension(False)
c = db.cursor()
c.execute('CREATE TABLE mytable (id integer, description text)')
c.execute('INSERT INTO mytable VALUES (1, "hello world, guys")')
c.execute('INSERT INTO mytable VALUES (2, "hello there everybody")')
c.execute('SELECT * FROM mytable WHERE editdist3(description, "hel o wrold guy") < 600')
print c.fetchall()
# Output: [(1, u'hello world, guys')]
Run Code Online (Sandbox Code Playgroud)

重要说明:距离editdist3已标准化,以便

100的值用于插入和删除,150用于替换


以下是在 Windows 上首先要做的事情:

  1. 下载https://sqlite.org/2016/sqlite-src-3110100.zip , https://sqlite.org/2016/sqlite-amalgamation-3110100.zip并解压

  2. 这里替换C:\Python27\DLLs\sqlite3.dll为新的sqlite3.dll。如果跳过此,你会得到一个后sqlite3.OperationalError: The specified procedure could not be found

  3. 跑:

    call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"  
    
    Run Code Online (Sandbox Code Playgroud)

    或者

    call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64
    cl /I sqlite-amalgamation-3110100/ sqlite-src-3110100/ext/misc/spellfix.c /link /DLL /OUT:spellfix.dll
    python test.py
    
    Run Code Online (Sandbox Code Playgroud)

    (使用MinGW,这将是:gcc -g -shared spellfix.c -I ~/sqlite-amalgation-3230100/ -o spellfix.dll

以下是如何在 Linux Debian 上执行此操作:

(基于这个答案

apt-get -y install unzip build-essential libsqlite3-dev
wget https://sqlite.org/2016/sqlite-src-3110100.zip
unzip sqlite-src-3110100.zip
gcc -shared -fPIC -Wall -Isqlite-src-3110100 sqlite-src-3110100/ext/misc/spellfix.c -o spellfix.so
python test.py
Run Code Online (Sandbox Code Playgroud)

以下是在使用较旧 Python 版本的 Linux Debian 上执行此操作的方法:

如果您的发行版的 Python 有点旧,它将需要另一种方法。由于sqlite3模块内置在 Python 中,升级它似乎并不简单pip install --upgrade pysqlite只会升级 pysqlite 模块,而不是底层的 SQLite 库)。因此,例如如果是 3.8.2 ,则此方法有效import sqlite3; print sqlite3.sqlite_version

wget https://www.sqlite.org/src/tarball/27392118/SQLite-27392118.tar.gz
tar xvfz SQLite-27392118.tar.gz
cd SQLite-27392118 ; sh configure ; make sqlite3.c ; cd ..
gcc -g -fPIC -shared SQLite-27392118/ext/misc/spellfix.c -I SQLite-27392118/src/ -o spellfix.so
python test.py   # [(1, u'hello world, guys')]
Run Code Online (Sandbox Code Playgroud)


小智 5

我实现了与距离相关的函数(Damerau-Levenshtein、Jaro-Winkler、最长公共子串和子序列)作为 SQLite 运行时可加载扩展。支持任何 UTF-8 字符串。

https://github.com/schiffma/distlib

  • 欢迎来到 StackOverflow,并为这个精美的 Github 存储库感到高兴。为了使这个答案在这里成为一个有用的答案(不接受仅链接的答案),您是否可以添加更多上下文,并使用示例可重现代码(在几行 Python 中)展示如何实际使用它? (4认同)