2013-09-04 45 views
15

Tôi đang cố gắng lưu trữ một mảng khối lượng khoảng 1000 phao trong cơ sở dữ liệu sqlite3 nhưng tôi vẫn gặp lỗi "InterfaceError: Tham số ràng buộc lỗi 1 - có thể là loại không được hỗ trợ".Python chèn mảng numpy vào cơ sở dữ liệu sqlite3

Tôi đã có ấn tượng về loại dữ liệu BLOB có thể là bất kỳ thứ gì nhưng chắc chắn nó không hoạt động với mảng có nhiều mảng. Dưới đây là những gì tôi đã thử:

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() 

Có mô-đun nào khác mà tôi có thể sử dụng để đưa mảng vào bảng? Hoặc tôi có thể chuyển đổi mảng numpy vào một hình thức khác trong Python (như một danh sách hoặc chuỗi tôi có thể tách) mà sqlite sẽ chấp nhận? Hiệu suất không phải là ưu tiên. Tôi chỉ muốn nó làm việc!

Cảm ơn!

+0

Không biết, nhưng cố gắng để chuyển đổi vào danh sách? np.arange (1000) .tolist() – reptilicus

+0

hoặc có thể là json.dumps (np.arange (1000) .tolist()) – reptilicus

Trả lời

23

Bạn có thể đăng ký một loại array dữ liệu mới với sqlite3:

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)") 

Với thiết lập này, bạn chỉ có thể chèn các mảng NumPy không có thay đổi trong cú pháp :

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

Và truy xuất mảng trực tiếp từ sq lite dưới dạng mảng 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'> 
+6

Điều này rất hữu ích cho tôi. Chỉ cần để làm cho rõ ràng cho người khác, kết nối phải được mở ra với các tùy chọn detect_types = sqlite3.PARSE_DECLTYPES Tôi chạy vào rắc rối bởi vì tôi quên giữ điều đó in –

+0

Đẹp! Cảm ơn! –

+0

Đó 'đệm' phá vỡ 3.x khả năng tương thích (đó là một điều lạ để làm trong mã có sử dụng' io' module và 'print' như là một chức năng), và nó không có vẻ là cần thiết trong 2,7 của tôi. 6 hoặc 2.7.9. Có lẽ các phiên bản cũ hơn của 'sqlite3' có vấn đề với nó, nhưng nếu 2.6+ làm việc mà không có nó, có lẽ bạn nên loại bỏ nó. Xem thêm [câu hỏi này] (http://stackoverflow.com/questions/30063437/trouble-storing-numpy-array-in-sqlite3-with-python/30064209#30064209). – abarnert

4

này làm việc cho tôi:

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]) 
2

Bước nhảy vọt thứ hai đã gần nhưng tôi vẫn nhận được tự động truyền tới chuỗi. Ngoài ra, nếu bạn xem bài đăng khác này: a fun debate on using buffer or Binary to push non text data into sqlite bạn thấy rằng cách tiếp cận được tài liệu là tránh toàn bộ bộ đệm và sử dụng đoạn mã này.

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

tôi đã không nặng nề thử nghiệm điều này trong python 3, nhưng có vẻ như để làm việc trong python 2.7

+0

Cảm ơn bạn đã cải tiến này; điều này dường như làm việc trong cả Python3 và 2,7. Tôi cũng đã cập nhật câu trả lời của mình để sử dụng 'sqlite3.Binary'. – unutbu

1

Tôi nghĩ rằng matlab định dạng là một cách thực sự thuận tiện để lưu trữ và lấy mảng NumPy. Thực sự là nhanhdấu chân đĩa và bộ nhớ hoàn toàn giống nhau.

Load/Save/Disk Comparison

(hình ảnh từ mverleg benchmarks)

Nhưng nếu vì lý do nào đó bạn cần lưu trữ các mảng NumPy vào SQLite Tôi đề nghị thêm một số khả năng nén.

Các dòng thêm từ unutbu code đang khá đơn giản

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) 

Kết quả thử nghiệm với cơ sở dữ liệu MNIST cho đó là:

$ ./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 
``` 

sử dụng zlib, và

$ ./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 

sử dụng bz2

So sánh định dạng Matlab V5 với bz2 trên SQLite, nén bz2 là khoảng 2.8, nhưng thời gian truy cập khá dài so với định dạng Matlab (gần như ngay lập tức so với hơn 30 giây). Có lẽ chỉ đáng giá đối với các cơ sở dữ liệu thực sự khổng lồ mà quá trình học tập tốn nhiều thời gian hơn thời gian truy cập hoặc nơi mà dấu chân cơ sở dữ liệu là cần thiết càng nhỏ càng tốt.

Cuối cùng lưu ý rằng tỷ lệ bipz/zlib là khoảng 3.7 và zlib/matlab yêu cầu thêm 30% không gian.

Mã đầy đủ nếu bạn muốn chơi mình là:

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() 
Các vấn đề liên quan