2017-03-23 22 views
1

辞書マッピングインデックスがfloatの値を含むJSON文字列があります。これはベクトルの代表です。例えば、このベクターを保存する最も効率的な方法は?

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

インデックス0インデックス22.562.42を有するサイズ10000のベクトルを表します。このベクトルの他の値はすべて4.55です。

は効率的ですこのデータ構造を表す方法はありますか? scipy.sparse私を助けますか?私の主なアプリケーションは、密な表現をすばやく作成することですが、あらかじめ(そのようなベクターが多いので)あらかじめメモリに保存したくありません。

+0

はすでにかなりのスペース効率のよい(JSONを...)である(数理?)操作のどのようなあなたは、このデータ型をサポートするために必要なのですか? – 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())は)のリストを与えますタプル。 Newer numpyバージョンでは、floatをインデックスとして使用したくないため、整数キー値を保持するためにいくつかの手順を追加する必要があります。

これは最も単純であるかもしれない:

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

(私は同じキーの順序でkeysvaluesitemsの戻り値をとります)。

この小さなケースでは、単純な反復アプローチがより高速であると思われます。しかし、後者のものよりはるかに大きいものがおそらくもっと良いでしょう。クロスオーバーがどこで起こるかは予測できません。

scipy.sparseは2d行列を作成します。それはどんな種類のconstも実装しません。 (パンダのスパースにはそのような詰め物があります)。確かにdd['size']dd['distro']からsparseの行列を作ることができました。しかし、それは速度の利点を提供するかどうかわからない。

Tensorflowが本当のターゲットである場合は、その構築方法をさらに調べる必要があります。おそらく、numpyまたはsparseをまったく通過する必要はありません。その重要な属性である

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> 

constせず


このx

は、と scipyスパース行列として表すことができる

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行につき1つの値(+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の 'サイズ'のエントリの値が含まれている場合は、完全な数値配列を作成して保存することをおすすめします(例: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) 
関連する問題