2013-08-06 39 views
13

Tôi muốn lưu trữ khóa-giá trị phân cấp bằng Python, về cơ bản sẽ lưu trữ các từ điển vào tệp. Theo đó, tôi có nghĩa là bất kỳ loại cấu trúc từ điển nào, có thể chứa các từ điển khác, mảng numpy, các đối tượng Python có thể tuần tự hóa, v.v. Không chỉ vậy, tôi muốn nó lưu trữ các mảng không được tối ưu hóa không gian được tối ưu hóa và phát đẹp giữa Python 2 và 3.Lưu từ điển vào tập tin (thân thiện với Python và Python 2/3)

Dưới đây là các phương pháp tôi biết ở ngoài đó. Câu hỏi của tôi là những gì còn thiếu trong danh sách này và có một sự thay thế nào né tránh tất cả các bộ phận giao dịch của tôi không?

  • Python của pickle mô-đun (đối phó-breaker: thổi phồng kích thước của mảng NumPy rất nhiều)
  • save/savez/load NumPy của (đối phó-breaker: định dạng không tương thích trên Python 2/3)
  • PyTables replacement for numpy.savez (deal-breaker: chỉ xử lý các mảng numpy)
  • Sử dụng PyTables theo cách thủ công (deal-breaker: Tôi muốn điều này liên tục thay đổi mã nghiên cứu, vì vậy nó thực sự thuận tiện để có thể đưa từ điển vào tệp bằng cách gọi một hàm duy nhất)

Thay thế PyTables là numpy.savez là đầy hứa hẹn, vì tôi thích ý tưởng sử dụng hdf5 và nó nén các mảng gọn gàng thực sự hiệu quả, đó là một điểm cộng lớn. Tuy nhiên, nó không có bất kỳ loại cấu trúc từ điển nào.

Gần đây, những gì tôi đã làm là sử dụng một cái gì đó tương tự như thay thế PyTables, nhưng tăng cường nó để có thể lưu trữ bất kỳ loại mục. Điều này thực sự hoạt động khá tốt, nhưng tôi thấy bản thân mình lưu trữ các kiểu dữ liệu nguyên thủy trong các CArrays dài-1, một chút khó xử (và không rõ ràng đối với mảng dài-1 thực), mặc dù tôi đặt chunksize thành 1 để nó không mất nhiều không gian.

Có điều gì đó như thế đã xuất hiện ở đó không?

Cảm ơn!

+0

Bạn đã xem sử dụng hệ thống cơ sở dữ liệu NoSQL như MongoDB? –

+0

@Xaranke Đó là một ý tưởng tốt, nhưng tôi nghi ngờ nó sẽ cung cấp lưu trữ mảng hiệu quả numpy ... hoặc có thể nó sẽ? –

+0

Bạn có thể lưu một mảng có nhiều mảng như một đối tượng nhị phân như được hiển thị ở đây: http://stackoverflow.com/questions/6367589/saving-numpy-array-in-mongodb –

Trả lời

1

Sau khi hỏi điều này hai năm trước, tôi bắt đầu viết mã thay thế dựa trên HDF5 của riêng mình của dưa/np.save.Kể từ đó, nó đã trưởng thành thành một gói ổn định, vì vậy tôi nghĩ rằng tôi cuối cùng sẽ trả lời và chấp nhận câu hỏi của riêng tôi, vì nó là do thiết kế chính xác những gì tôi đang tìm kiếm:

+0

Làm 'nhập khẩu sâu dưới dạng dd; dd.io.save (tên tệp, {'dict1': dict1, 'dict2': dict2}, nén = ('blosc', 9)) 'là những gì tôi thấy hoạt động tốt. – wordsforthewise

+0

Bạn có thể thêm điều đó vào tệp cấu hình của mình, nếu đó là những gì bạn thích nhất quán: http://deepdish.readthedocs.io/en/latest/api_io.html –

+0

Phải, vậy trông giống như [io] {newline và 4 space} compression: (blosc, 9)? – wordsforthewise

0

Tôi đã thử chơi với np.memmap để lưu một loạt từ điển. Giả sử chúng ta có từ điển:

a = np.array([str({'a':1, 'b':2, 'c':[1,2,3,{'d':4}]}]) 

đầu tiên tôi đã cố gắng để trực tiếp lưu nó vào một memmap:

f = np.memmap('stack.array', dtype=dict, mode='w+', shape=(100,)) 
f[0] = d 
# CRASHES when reopening since it looses the memory pointer 

f = np.memmap('stack.array', dtype=object, mode='w+', shape=(100,)) 
f[0] = d 
# CRASHES when reopening for the same reason 

cách nó làm việc được chuyển đổi từ điển để một chuỗi:

f = np.memmap('stack.array', dtype='|S1000', mode='w+', shape=(100,)) 
f[0] = str(a) 

công trình này và sau đó bạn có thể eval(f[0]) để lấy lại giá trị.

Tôi không biết ưu điểm của phương pháp này so với các phương pháp khác, nhưng nó xứng đáng là một cái nhìn gần hơn.

+1

Cảm ơn, tôi đánh giá cao nó, nhưng tôi phải nói thẳng thắn về điều này và nói rằng đây là một ý tưởng tồi tệ trên nhiều tài khoản: Sử dụng str/eval như một phương tiện serialization không đáng tin cậy, và nó giới thiệu một lỗ hổng bảo mật lớn trong chương trình của bạn. Mặc định, các mảng lớn sẽ được in bằng '...', không thể bị xóa bỏ, các cá thể lớp nói chung sẽ không được xây dựng lại, v.v. Ngoài ra, ngay cả khi chúng được lưu trữ mọi thứ như biểu diễn chuỗi của chúng chiếm nhiều không gian hơn hơn bất kỳ phương pháp khác được thảo luận cho đến nay. –

+0

OK, tôi chỉ muốn thêm nỗ lực này vào đây ... –

2

Gần đây tôi đã gặp vấn đề tương tự, tôi đã viết một vài hàm để lưu nội dung của dicts vào một nhóm trong tệp PyTables và tải chúng trở lại vào dicts.

Chúng xử lý đệ quy từ điển và cấu trúc nhóm một cách đệ quy và xử lý các đối tượng có kiểu không được PyTables hỗ trợ bằng cách chọn chúng và lưu trữ chúng dưới dạng mảng chuỗi. Nó không hoàn hảo, nhưng ít nhất những thứ như mảng numpy sẽ được lưu trữ một cách hiệu quả.Ngoài ra còn có một kiểm tra bao gồm để tránh vô tình tải cấu trúc khổng lồ vào bộ nhớ khi đọc các nội dung nhóm trở lại vào một dict.

import tables 
import cPickle 

def dict2group(f, parent, groupname, dictin, force=False, recursive=True): 
    """ 
    Take a dict, shove it into a PyTables HDF5 file as a group. Each item in 
    the dict must have a type and shape compatible with PyTables Array. 

    If 'force == True', any existing child group of the parent node with the 
    same name as the new group will be overwritten. 

    If 'recursive == True' (default), new groups will be created recursively 
    for any items in the dict that are also dicts. 
    """ 
    try: 
     g = f.create_group(parent, groupname) 
    except tables.NodeError as ne: 
     if force: 
      pathstr = parent._v_pathname + '/' + groupname 
      f.removeNode(pathstr, recursive=True) 
      g = f.create_group(parent, groupname) 
     else: 
      raise ne 
    for key, item in dictin.iteritems(): 
     if isinstance(item, dict): 
      if recursive: 
       dict2group(f, g, key, item, recursive=True) 
     else: 
      if item is None: 
       item = '_None' 
      f.create_array(g, key, item) 
    return g 


def group2dict(f, g, recursive=True, warn=True, warn_if_bigger_than_nbytes=100E6): 
    """ 
    Traverse a group, pull the contents of its children and return them as 
    a Python dictionary, with the node names as the dictionary keys. 

    If 'recursive == True' (default), we will recursively traverse child 
    groups and put their children into sub-dictionaries, otherwise sub- 
    groups will be skipped. 

    Since this might potentially result in huge arrays being loaded into 
    system memory, the 'warn' option will prompt the user to confirm before 
    loading any individual array that is bigger than some threshold (default 
    is 100MB) 
    """ 

    def memtest(child, threshold=warn_if_bigger_than_nbytes): 
     mem = child.size_in_memory 
     if mem > threshold: 
      print '[!] "%s" is %iMB in size [!]' % (child._v_pathname, mem/1E6) 
      confirm = raw_input('Load it anyway? [y/N] >>') 
      if confirm.lower() == 'y': 
       return True 
      else: 
       print "Skipping item \"%s\"..." % g._v_pathname 
     else: 
      return True 
    outdict = {} 
    for child in g: 
     try: 
      if isinstance(child, tables.group.Group): 
       if recursive: 
        item = group2dict(f, child) 
       else: 
        continue 
      else: 
       if memtest(child): 
        item = child.read() 
        if isinstance(item, str): 
         if item == '_None': 
          item = None 
       else: 
        continue 
      outdict.update({child._v_name: item}) 
     except tables.NoSuchNodeError: 
      warnings.warn('No such node: "%s", skipping...' % repr(child)) 
      pass 
    return outdict 

Nó cũng đáng nói joblib.dumpjoblib.load, mà đánh dấu vào tất cả các hộp của bạn ngoài Python 2/3 chéo tương thích. Dưới mui xe, họ sử dụng np.save cho các mảng numpy và cPickle cho mọi thứ khác.

+0

Cảm ơn, điều này rất giống với những gì tôi đã bắt đầu thực hiện. Ngoại trừ catch-all của tôi là một CArray có kích thước 1 mà tôi tạo với 'Atom.from_dtype (np.dtype (type (obj))' .Từ PyTables tự nhiên hỗ trợ kiểu 'object' numpy, các lớp sẽ nhận được kiểu đó và chỉ làm việc Tất nhiên PyTables sẽ chọn nó dưới mui xe, nhưng nó ẩn nó cho tôi nên mã tôi sử dụng thực sự ngắn. Nó không rõ ràng với các mảng 'hợp lệ' (1), nhưng đó là một vấn đề nhỏ mà tôi có thể –

+0

Rất có thể đó là lần đầu tiên tôi sử dụng kiểu 'np.object' –

0

Tôi hoàn toàn khuyên bạn nên sử dụng cơ sở dữ liệu đối tượng python như ZODB. Nó có vẻ khá phù hợp cho tình huống của bạn, xem xét bạn lưu trữ các đối tượng (theo nghĩa đen bất cứ điều gì bạn thích) vào một từ điển - điều này có nghĩa là bạn có thể lưu trữ từ điển bên trong từ điển. Tôi đã sử dụng nó trong một loạt các vấn đề, và điều tốt đẹp là bạn chỉ có thể trao cho ai đó tệp cơ sở dữ liệu (tệp có phần mở rộng .fs). Với điều này, họ sẽ có thể đọc nó và thực hiện bất kỳ truy vấn nào họ muốn và sửa đổi các bản sao cục bộ của riêng họ. Nếu bạn muốn có nhiều chương trình cùng lúc truy cập vào cùng một cơ sở dữ liệu, tôi chắc chắn sẽ xem xét ZEO.

Chỉ cần một ví dụ ngớ ngẩn như thế nào để bắt đầu:

from ZODB import DB 
from ZODB.FileStorage import FileStorage 
from ZODB.PersistentMapping import PersistentMapping 
import transaction 
from persistent import Persistent 
from persistent.dict import PersistentDict 
from persistent.list import PersistentList 

# Defining database type and creating connection. 
storage = FileStorage('/path/to/database/zodbname.fs') 
db = DB(storage) 
connection = db.open() 
root = connection.root() 

# Define and populate the structure. 
root['Vehicle'] = PersistentDict() # Upper-most dictionary 
root['Vehicle']['Tesla Model S'] = PersistentDict() # Object 1 - also a dictionary 
root['Vehicle']['Tesla Model S']['range'] = "208 miles" 
root['Vehicle']['Tesla Model S']['acceleration'] = 5.9 
root['Vehicle']['Tesla Model S']['base_price'] = "$71,070" 
root['Vehicle']['Tesla Model S']['battery_options'] = ["60kWh","85kWh","85kWh Performance"] 
# more attributes here 

root['Vehicle']['Mercedes-Benz SLS AMG E-Cell'] = PersistentDict() # Object 2 - also a dictionary 
# more attributes here 

# add as many objects with as many characteristics as you like. 

# commiting changes; up until this point things can be rolled back 
transaction.get().commit() 
transaction.get().abort() 
connection.close() 
db.close() 
storage.close() 

Khi cơ sở dữ liệu được tạo ra nó rất dễ sử dụng. Vì nó là một cơ sở dữ liệu đối tượng (một cuốn từ điển), bạn có thể truy cập vào đối tượng rất dễ dàng:

#after it's opened (lines from the very beginning, up to and including root = connection.root()) 
>> root['Vehicles']['Tesla Model S']['range'] 
'208 miles' 

Bạn cũng có thể hiển thị tất cả các phím (và làm tất cả những thứ khác chuẩn từ điển mà bạn có thể muốn làm):

>> root['Vehicles']['Tesla Model S'].keys() 
['acceleration', 'range', 'battery_options', 'base_price'] 

Điều cuối cùng tôi muốn đề cập là các phím có thể được thay đổi: Changing the key value in python dictionary. Giá trị cũng có thể được thay đổi - vì vậy nếu kết quả nghiên cứu của bạn thay đổi bởi vì bạn thay đổi phương thức hoặc thứ gì đó bạn không phải khởi động toàn bộ cơ sở dữ liệu từ đầu (đặc biệt là nếu mọi thứ khác vẫn ổn). Hãy cẩn thận khi thực hiện cả hai điều này. Tôi đưa ra các biện pháp an toàn trong mã cơ sở dữ liệu của mình để đảm bảo rằng tôi biết các nỗ lực của mình để ghi đè khóa hoặc giá trị.

** THÊM **

# added imports 
import numpy as np 
from tempfile import TemporaryFile 
outfile = TemporaryFile() 

# insert into definition/population section 
np.save(outfile,np.linspace(-1,1,10000)) 
root['Vehicle']['Tesla Model S']['arraydata'] = outfile 

# check to see if it worked 
>>> root['Vehicle']['Tesla Model S']['arraydata'] 
<open file '<fdopen>', mode 'w+b' at 0x2693db0> 

outfile.seek(0)# simulate closing and re-opening 
A = np.load(root['Vehicle']['Tesla Model S']['arraydata']) 

>>> print A 
array([-1.  , -0.99979998, -0.99959996, ..., 0.99959996, 
    0.99979998, 1.  ]) 

Bạn cũng có thể sử dụng numpy.savez() cho tiết kiệm nén của nhiều mảng NumPy trong cùng một cách chính xác này.

+0

Điều này có vẻ độc đáo để lưu trữ các từ điển.Tuy nhiên, tôi không thấy nhiều về việc lưu trữ các mảng numpy Vì nhiều lá cây từ điển của tôi là các mảng có nhiều mảng, nên điều quan trọng là chúng được lưu trữ tối ưu. hỗ trợ, tốt nhất là nên quay trở lại để làm sạch dữ liệu, điều này không phụ thuộc vào Tôi rất sợ. –

+0

Hai điều: 1) Tại sao bạn cho rằng nó không thể lưu trữ các mảng có nhiều mảng? Là đối số của các phím của tôi, tôi có thể dễ dàng lưu trữ một mảng numpy. 2) Tôi chỉ thử nghiệm numpy.save() và nó có vẻ là làm việc trong phiên bản python-3.3.1 của tôi tốt. Bạn có nói cú pháp là khác nhau? Nếu có, có các công cụ (http://docs.python.org/2/library/2to3.html) để chuyển đổi giữa hai công cụ. Nếu bạn vẫn gặp vấn đề tại sao bạn không kiểm tra phiên bản của python (import platform \ n platform.python_version()) mà nó mặc định, và điều chỉnh cú pháp trong câu lệnh điều kiện? – astromax

+0

Bạn thậm chí có thể lưu trữ một tập tin như một đối số cho một khóa, cho phép bạn nén mảng numpy của bạn bằng cách sử dụng numpy.save(). – astromax

0

Đây không phải là câu trả lời trực tiếp. Dù sao, bạn cũng có thể quan tâm đến JSON. Hãy xem số 13.10. Serializing Datatypes Unsupported by JSON. Nó cho thấy làm thế nào để mở rộng định dạng cho các loại unsuported.

Toàn bộ chương từ "Dive vào Python 3" của Mark Pilgrim chắc chắn là một đọc tốt trong ít nhất phải biết ...

Cập nhật: Có thể một ý tưởng không liên quan, nhưng ... Tôi đã đọc ở đâu đó, đó là một trong những lý do tại sao XML cuối cùng được chấp nhận cho trao đổi dữ liệu trong môi trường không đồng nhất là một số nghiên cứu so sánh định dạng nhị phân chuyên biệt với XML nén. Kết luận cho bạn có thể được sử dụng có thể không phải như vậy không gian giải pháp hiệu quả và nén nó thông qua zip hoặc một thuật toán nổi tiếng. Sử dụng thuật toán đã biết sẽ giúp khi bạn cần gỡ lỗi (để giải nén và sau đó nhìn vào tệp văn bản bằng mắt).

+0

Tôi nghĩ rằng đây là một giải pháp tuyệt vời (ngang bằng với ZODB và thích) nếu mối quan tâm chính là lưu cấu trúc từ điển với dữ liệu đơn giản. Tuy nhiên, sự tập trung của tôi là khi các lá thường là mảng rất lớn, do đó chúng sẽ làm hỏng hoàn toàn khả năng đọc của con người đối với các tệp có khối byte lớn. Nếu không có khía cạnh có thể đọc được của con người, tôi e rằng một giải pháp sử dụng JSON/XML không phải là do hít phải không gian lưu trữ được tối ưu hóa, mặc dù đã thêm nén (ít nhất là so với PyTables). –

Các vấn đề liên quan