【问题标题】:Python insert numpy array into sqlite3 databasePython将numpy数组插入sqlite3数据库
【发布时间】:2013-09-08 09:55:24
【问题描述】:

我正在尝试在 sqlite3 数据库中存储大约 1000 个浮点数的 numpy 数组,但我不断收到错误“InterfaceError:错误绑定参数 1 - 可能不受支持的类型”。

我的印象是 BLOB 数据类型可以是任何东西,但它绝对不适用于 numpy 数组。这是我尝试过的:

import sqlite3 as sql
import numpy as np
con = sql.connect('test.bd',isolation_level=None)
cur = con.cursor()
cur.execute("CREATE TABLE foobar (id INTEGER PRIMARY KEY, array BLOB)")
cur.execute("INSERT INTO foobar VALUES (?,?)", (None,np.arange(0,500,0.5)))
con.commit()

我可以使用另一个模块将 numpy 数组放入表中吗?或者我可以将numpy数组转换为sqlite可以接受的Python中的另一种形式(比如我可以拆分的列表或字符串)吗?性能不是优先事项。我只是想让它工作!

谢谢!

【问题讨论】:

  • 不知道,但尝试转换为列表? np.arange(1000).tolist()
  • 或者可能是 json.dumps(np.arange(1000).tolist())

标签: python numpy sqlite


【解决方案1】:

指定的其他方法对我不起作用。好吧,现在似乎有一个numpy.tobytes 方法和一个numpy.fromstring(适用于字节字符串),但已被弃用,推荐的方法是numpy.frombuffer

import sqlite3
import numpy as np

sqlite3.register_adapter(np.array, adapt_array)    
sqlite3.register_converter("array", convert_array)

来到肉和土豆,

def adapt_array(arr):
    return arr.tobytes()

def convert_array(text):
    return np.frombuffer(text)

我已经在我的应用程序中对其进行了测试,它在 Python 3.7.3numpy 1.16.2 上对我很有效

numpy.fromstringDeprecationWarning: The binary mode of fromstring is deprecated, as it behaves surprisingly on unicode inputs. Use frombuffer instead 提供相同的输出

【讨论】:

  • 我喜欢这个,但如果你有不同的 dtype,一些 float 和一些 float32,最好标准化:arr.astype('float32').tobytes()np.frombuffer(text, dtype='float32')
【解决方案2】:

Happy Leap Second 已经很接近了,但我一直在自动转换为字符串。 此外,如果您查看其他帖子:a fun debate on using buffer or Binary to push non text data into sqlite,您会看到记录在案的方法是避免一起使用缓冲区并使用这段代码。

def adapt_array(arr):
    out = io.BytesIO()
    np.save(out, arr)
    out.seek(0)
    return sqlite3.Binary(out.read())

我没有在 python 3 中对此进行过大量测试,但它似乎在 python 2.7 中有效

【讨论】:

  • 感谢您的改进;这似乎在 Python3 和 2.7 中都有效。我也更新了我的答案以使用sqlite3.Binary
【解决方案3】:

我认为matlab 格式是一种非常方便的存储和检索 numpy 数组的方法。真的磁盘和内存占用完全一样。

(图片来自mverleg benchmarks

但如果出于任何原因需要将 numpy 数组存储到 SQLite 中,我建议添加一些压缩功能。

unutbu 代码中的额外行非常简单

compressor = 'zlib'  # zlib, bz2

def adapt_array(arr):
    """
    http://stackoverflow.com/a/31312102/190597 (SoulNibbler)
    """
    # zlib uses similar disk size that Matlab v5 .mat files
    # bz2 compress 4 times zlib, but storing process is 20 times slower.
    out = io.BytesIO()
    np.save(out, arr)
    out.seek(0)
    return sqlite3.Binary(out.read().encode(compressor))  # zlib, bz2

def convert_array(text):
    out = io.BytesIO(text)
    out.seek(0)
    out = io.BytesIO(out.read().decode(compressor))
    return np.load(out)

使用 MNIST 数据库测试的结果是:

$ ./test_MNIST.py
[69900]:  99% remain: 0 secs   
Storing 70000 images in 379.9 secs
Retrieve 6990 images in 9.5 secs
$ ls -lh example.db 
-rw-r--r-- 1 agp agp 69M sep 22 07:27 example.db
$ ls -lh mnist-original.mat 
-rw-r--r-- 1 agp agp 53M sep 20 17:59 mnist-original.mat
```

使用zlib,并且

$ ./test_MNIST.py
[69900]:  99% remain: 12 secs   
Storing 70000 images in 8536.2 secs
Retrieve 6990 images in 37.4 secs
$ ls -lh example.db 
-rw-r--r-- 1 agp agp 19M sep 22 03:33 example.db
$ ls -lh mnist-original.mat 
-rw-r--r-- 1 agp agp 53M sep 20 17:59 mnist-original.mat

使用bz2

Matlab V5 格式与 SQLite 上的bz2 进行比较,bz2 压缩率约为 2.8,但与 Matlab 格式相比,访问时间相当长(几乎是瞬间 vs 超过 30 秒)。 Maybe 仅适用于真正庞大的数据库,其中学习过程比访问时间更耗时,或者数据库占用空间需要尽可能小。

最后请注意,bipz/zlib 的比率约为 3.7,而zlib/matlab 需要多 30% 的空间。

如果你想自己玩,完整的代码是:

import sqlite3
import numpy as np
import io

compressor = 'zlib'  # zlib, bz2

def adapt_array(arr):
    """
    http://stackoverflow.com/a/31312102/190597 (SoulNibbler)
    """
    # zlib uses similar disk size that Matlab v5 .mat files
    # bz2 compress 4 times zlib, but storing process is 20 times slower.
    out = io.BytesIO()
    np.save(out, arr)
    out.seek(0)
    return sqlite3.Binary(out.read().encode(compressor))  # zlib, bz2

def convert_array(text):
    out = io.BytesIO(text)
    out.seek(0)
    out = io.BytesIO(out.read().decode(compressor))
    return np.load(out)

sqlite3.register_adapter(np.ndarray, adapt_array)
sqlite3.register_converter("array", convert_array)

dbname = 'example.db'
def test_save_sqlite_arrays():
    "Load MNIST database (70000 samples) and store in a compressed SQLite db"
    os.path.exists(dbname) and os.unlink(dbname)
    con = sqlite3.connect(dbname, detect_types=sqlite3.PARSE_DECLTYPES)
    cur = con.cursor()
    cur.execute("create table test (idx integer primary key, X array, y integer );")

    mnist = fetch_mldata('MNIST original')

    X, y =  mnist.data, mnist.target
    m = X.shape[0]
    t0 = time.time()
    for i, x in enumerate(X):
        cur.execute("insert into test (idx, X, y) values (?,?,?)",
                    (i, y, int(y[i])))
        if not i % 100 and i > 0:
            elapsed = time.time() - t0
            remain = float(m - i) / i * elapsed
            print "\r[%5d]: %3d%% remain: %d secs" % (i, 100 * i / m, remain),
            sys.stdout.flush()

    con.commit()
    con.close()
    elapsed = time.time() - t0
    print
    print "Storing %d images in %0.1f secs" % (m, elapsed)

def test_load_sqlite_arrays():
    "Query MNIST SQLite database and load some samples"
    con = sqlite3.connect(dbname, detect_types=sqlite3.PARSE_DECLTYPES)
    cur = con.cursor()

    # select all images labeled as '2'
    t0 = time.time()
    cur.execute('select idx, X, y from test where y = 2')
    data = cur.fetchall()
    elapsed = time.time() - t0
    print "Retrieve %d images in %0.1f secs" % (len(data), elapsed)


if __name__ == '__main__':
    test_save_sqlite_arrays()
    test_load_sqlite_arrays()

【讨论】:

  • 我收到这个错误,有什么想法吗?: LookupError: 'zlib' is not a text encoding;使用 codecs.decode() 处理任意编解码器
【解决方案4】:

你可以用sqlite3注册一个新的array数据类型:

import sqlite3
import numpy as np
import io

def adapt_array(arr):
    """
    http://stackoverflow.com/a/31312102/190597 (SoulNibbler)
    """
    out = io.BytesIO()
    np.save(out, arr)
    out.seek(0)
    return sqlite3.Binary(out.read())

def convert_array(text):
    out = io.BytesIO(text)
    out.seek(0)
    return np.load(out)


# Converts np.array to TEXT when inserting
sqlite3.register_adapter(np.ndarray, adapt_array)

# Converts TEXT to np.array when selecting
sqlite3.register_converter("array", convert_array)

x = np.arange(12).reshape(2,6)

con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.cursor()
cur.execute("create table test (arr array)")

使用此设置,您可以简单地插入 NumPy 数组,而无需更改语法:

cur.execute("insert into test (arr) values (?)", (x, ))

并直接从 sqlite 中检索数组作为 NumPy 数组:

cur.execute("select arr from test")
data = cur.fetchone()[0]

print(data)
# [[ 0  1  2  3  4  5]
#  [ 6  7  8  9 10 11]]
print(type(data))
# <type 'numpy.ndarray'>

【讨论】:

  • 这对我很有用。只是为了让其他人清楚,必须使用选项 detect_types=sqlite3.PARSE_DECLTYPES 打开连接我遇到了麻烦,因为我忘了保留它。
  • buffer 破坏了 3.x 兼容性(这在使用 io 模块和 print 作为函数的代码中是一件很奇怪的事情),而且似乎没有在我的 2.7.6 或 2.7.9 中是必需的。也许旧版本的 sqlite3 有问题,但如果 2.6+ 没有它,你应该删除它。另请参阅this question
  • 另外,如果是2.x的问题,bytearray能解决问题吗?因为这可以移植到 3.x(无需像定义 try: buffer except NameError: buffer=bytesdef buffer(x): return x 之类的东西)。
  • 我遵循了这个解决方案,但是在将值存储到 sqlite3 db 后无法将值加载回内存变量。
  • 谢谢,我找到了问题的根本原因。我在调用 sqlite3.connect() 时没有设置 detect_types=sqlite3.PARSE_DECLTYPES,添加后它也适用于我的系统。再次感谢!
【解决方案5】:

这对我有用:

import sqlite3 as sql
import numpy as np
import json
con = sql.connect('test.db',isolation_level=None)
cur = con.cursor()
cur.execute("DROP TABLE FOOBAR")
cur.execute("CREATE TABLE foobar (id INTEGER PRIMARY KEY, array BLOB)")
cur.execute("INSERT INTO foobar VALUES (?,?)", (None, json.dumps(np.arange(0,500,0.5).tolist())))
con.commit()
cur.execute("SELECT * FROM FOOBAR")
data = cur.fetchall()
print data
data = cur.fetchall()
my_list = json.loads(data[0][1])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-25
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    相关资源
    最近更新 更多