2017-03-23 70 views
1

我有一個JSON字符串,它包含一個到float值的字典映射索引。這是一個矢量的代表。例如,最有效的方法來存儲這個矢量?

{ 
    'distro': {0: 2.42, 3: 2.56}, 
    'constant': 4.55 
    'size': 10000 
} 

表示關於索引2具有2.42上索引02.56大小10000的向量。此向量中的所有其他值都是4.55

什麼是最有效的表示這種數據結構的方式? scipy.sparse會幫我嗎?我的主要應用是快速創建密集表示,但我不希望事先將它們存儲在內存中(因爲有很多這樣的向量)。

+0

你有什麼已經非常有效的空間(JSON ..)你需要這種數據類型支持什麼樣的(mathmatical?)操作? – Aaron

+0

我需要快速創建它的密集表示。運行'for'循環的速度太慢了 – martianwars

+0

一旦你創建了這個數組,你需要如何處理它,它需要訪問它的元素?並非所有的稀疏數組類都支持numpy的所有ufuncs和其他函數(numpy.dot是一個特別值得注意的函數) – Aaron

回答

1

我想,你迭代的方式是這樣的:

In [204]: dd = { 
    ...:  'distro': {0: 2.42, 3: 2.56}, 
    ...:  'constant': 4.55, 
    ...:  'size': 10, 
    ...: } 
In [205]: dd 
Out[205]: {'constant': 4.55, 'distro': {0: 2.42, 3: 2.56}, 'size': 10} 

In [207]: x = np.zeros(dd['size']) 
In [208]: x[:] = dd['constant'] 
In [210]: for i,v in dd['distro'].items(): 
    ...:  x[i] = v 
In [211]: x 
Out[211]: array([ 2.42, 4.55, 4.55, 2.56, 4.55, 4.55, 4.55, 4.55, 4.55, 4.55]) 

x[:]一種替代,是x.fill(dd['constant']),但不認爲有速度太大的差別。

這裏是沒有明確的迭代從字典中設置值的一種方式:

In [221]: ddvals = np.array(list(dd['distro'].items()),dtype='i,f') 
In [222]: ddvals 
Out[222]: 
array([(0, 2.42000008), (3, 2.55999994)], 
     dtype=[('f0', '<i4'), ('f1', '<f4')]) 
In [223]: x[ddvals['f0']]=ddvals['f1'] 
In [224]: x 
Out[224]: 
array([ 2.42000008, 4.55  , 4.55  , 2.55999994, 4.55  , 
     4.55  , 4.55  , 4.55  , 4.55  , 4.55  ]) 

或無結構數組:

In [225]: vals = np.array(list(dd['distro'].items())) 
In [226]: vals 
Out[226]: 
array([[ 0. , 2.42], 
     [ 3. , 2.56]]) 
In [227]: x[vals[:,0]] = vals[:,1] 
... 
IndexError: arrays used as indices must be of integer (or boolean) type 
In [228]: x[vals[:,0].astype(int)] = vals[:,1] 
In [229]: x 
Out[229]: array([ 2.42, 4.55, 4.55, 2.56, 4.55, 4.55, 4.55, 4.55, 4.55, 4.55]) 

字典items()(或PY3 list(items()))給出的列表元組。較新的numpy版本不喜歡使用浮點數作爲索引,所以我們必須添加幾個步驟來保留整數鍵值。

這可能是最簡單的:

x[list(dd['distro'].keys())] = list(dd['distro'].values()) 

(我假設在同一個鍵順序keysvaluesitems返回值)。

對於這個小案例,我懷疑普通的迭代方法更快。但是後者的更大一些可能更好。我無法預測交叉發生在哪裏。

scipy.sparse構成2d矩陣。它沒有實現任何種類的const填充。 (熊貓稀疏確實有這樣的填充)。我們當然可以從dd['size']dd['distro']構建一個sparse矩陣。但我不知道它是否會提供任何速度優勢。

如果Tensorflow是您的真正目標,那麼您可能需要更多地瞭解其構造方法。也許你根本不需要通過numpysparse


x,而不const可以被表示爲具有一個scipy稀疏矩陣:

In [247]: Xo = sparse.coo_matrix([x]) 
In [248]: Xo 
Out[248]: 
<1x10 sparse matrix of type '<class 'numpy.float64'>' 
    with 2 stored elements in COOrdinate format> 

它的關鍵屬性是:

In [249]: Xo.data 
Out[249]: array([ 2.42, 2.56]) 
In [250]: Xo.row 
Out[250]: array([0, 0], dtype=int32) 
In [251]: Xo.col 
Out[251]: array([0, 3], dtype=int32) 
In [252]: Xo.shape 
Out[252]: (1, 10) 

Xr=Xo.tocsr()csr格式是類似的,除了row屬性被替換爲indptr數組,每行有一個值(+1),所以它不會隨着非零項的數量而增長。它用於最稀疏的數學。

還有一個dok格式,這實際上是一本字典的子類:

In [258]: dict(Xo.todok()) 
Out[258]: {(0, 0): 2.4199999999999999, (0, 3): 2.5600000000000001} 

如果輸入有效json,你將需要轉換的索引鍵爲整數。

In [281]: jstr 
Out[281]: '{"distro": {"0": 2.42, "3": 2.56}, "constant": 4.55, "size": 10}' 
In [282]: jdd = json.loads(jstr) 
In [283]: jdd 
Out[283]: {'constant': 4.55, 'distro': {'0': 2.42, '3': 2.56}, 'size': 10} 
In [284]: list(jdd['distro'].keys()) 
Out[284]: ['0', '3'] 
In [285]: np.array(list(jdd['distro'].keys()),int) 
Out[285]: array([0, 3]) 
In [286]: np.array(list(jdd['distro'].values())) 
Out[286]: array([ 2.42, 2.56]) 

我從SO搜索的印象是,json.load一樣快,如果不是eval快。它必須解析一個更簡單的語法。

python eval vs ast.literal_eval vs JSON decode

如果你能處理json字符串,並將其存儲在某種中間數據結構有幾種可能性。這些向量如何'稀疏'?如果字典中幾乎包含所有1000個「尺寸」條目的值,則最好構建完整的numpy數組並保存(例如使用np.save/load對)。

如果它是稀疏的(比如10%的值是非const),保存2個索引和值數組可能會更有意義(超出285和284)。要麼保持它們分離,要麼將它們加入到我之前生成的結構化數組中。

+0

你應該看到關於主要評論線程的討論,OP的問題實際上是解析json的速度而不是數組的分配.... – Aaron

+0

如果輸入是有效的'json',那麼'json.loads'可能與'eval'一樣快,http://stackoverflow.com/a/20276991/901925。然而'distro'鍵將是字符串,而不是有效的'json'中的整數。 – hpaulj

1

基於我們的討論,我想出了一個將json轉換成簡單(快速)解析二進制格式的例子。請參閱註釋說明。我使用BytesIO進行測試,但您可能只需使用以'rb'模式打開的文件。

import numpy as np 
import struct 
import io 


test_json = '''{ 
    'distro': {0: 2.42, 3: 2.56}, 
    'constant': 4.55, 
    'size': 10000 
}''' 

#this will be slow but only have to take place once.. 
def json_to_bin(test_json): 
    j = eval(test_json) #json provided was not well formed json, but was well formed python dict.. 
    const = j['constant'] 
    size = j['size'] 
    n = len(j['distro']) 
    keys = j['distro'].keys() 
    vals = [j['distro'][key] for key in keys] 

    buf = io.BytesIO() #dummy file like object 
    #struct.pack args: 
     # format_string, *values_described_in_format_string 
    buf.write(struct.pack("dii{0}i{0}d".format(n), const, size, n, *keys, *vals)) 
    return buf 

def bin_to_arr(buf): 
    buf.seek(0) 
    #unpack our first three values 
    #most important is n so we can unpack the right number of keys and values 
    const, size, n = struct.unpack("dii", buf.read(16)) #struct.calcsize('dii') = 16 
    #unpack keys 
    fmt = "{}i".format(n) 
    nbytes = struct.calcsize(fmt) 
    keys = np.array(struct.unpack(fmt, buf.read(nbytes))) #returns array (for indexing) 
    #unpack vals 
    fmt = "{}d".format(n) 
    nbytes = struct.calcsize(fmt) 
    vals = struct.unpack(fmt, buf.read(nbytes)) #returns tuple 
    #create ndarray 
    arr = np.empty(size) 
    arr.fill(const) 
    arr[keys] = vals 
    return arr 

print(test_json) 
print('=======binary========') 
buf = json_to_bin(test_json) 
buf.seek(0) 
print(buf.read()) 
arr = bin_to_arr(buf) 
相關問題