簡體   English   中英

Python 中的二級內存索引表示

[英]Secondary in-memory index representations in Python

我正在尋找一種有效的解決方案,使用 numpy 和箭頭等高級優化數學包在 Python 中構建二級內存索引。 出於性能原因,我排除了熊貓。

定義

“二級索引包含要被索引的屬性的每個現有值的條目。這個條目可以被看作是一個鍵/值對,其中屬性值作為鍵,值是指向基表中所有記錄的指針列表。有這個價值。” - 合資企業。 德席爾瓦等人。 (2017)

讓我們舉一個簡單的例子,我們可以稍后對其進行擴展以產生一些基准:

import numpy as np

pk = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='uint32')
val = np.array([15.5, 3.75, 142.88, 142.88, None, None, None, 7.2, 2.1], dtype='float32')

有趣的是pyarrow.Array.dictionary_encode方法可以將值數組轉換為接近二級索引的字典編碼表示。

val.dictionary_encode()
Out[55]: 
<pyarrow.lib.DictionaryArray object at 0x7ff430d8b4d0>
-- dictionary:
  [
    15.5,
    3.75,
    142.88,
    nan,
    7.2,
    2.1
  ]
-- indices:
  [
    0,
    1,
    2,
    2,
    3,
    3,
    3,
    4,
    5
  ]

在這里打開了一個問題

因此,問題是使用 Python 數據結構在內存中構建二級索引以有效保存值和索引的速度有多快。 但這只是故事的一半,因為如果索引在過濾查詢(點、范圍)和轉換(在TRIADB 中重建行、列和關聯又名超)都能很好地服務,那么它就會很有用。 即使這里的快速描述也沒有涵蓋更新這種索引有多么容易。

出於多種原因,我已經開始研究一種可能的 PyArrow 開源解決方案。 排序的字典編碼表示通常應該滿足問題的要求,將較小的內存占用和更快/靈活的零復制 I/O 處理完美結合。

解決方案

我過去和現在都在尋找解決此問題的開源解決方案,但沒有找到滿足我胃口的解決方案。 這次我決定開始構建我自己的並公開討論它的實現,它也涵蓋了null情況,即丟失數據的情況。

請注意,二級索引非常接近鄰接列表表示,這是我的TRIADB項目中的核心元素,這也是尋找解決方案的主要原因。

讓我們從使用numpy一行代碼開始

idx = np.sort(np.array(list(zip(pk, val)), dtype=struct_type), order='val')

idx['val']
Out[68]: 
array([  2.1 ,   3.75,   7.2 ,  15.5 , 142.88, 142.88,    nan,    nan,
          nan], dtype=float32)

idx['pk']
Out[69]: array([8, 1, 7, 0, 2, 3, 4, 5, 6], dtype=uint32)

更快的解決方案(不太通用)

這是 pk 值在 range(n) 中的特殊但完全有效的情況

idx_pk = np.argsort(val)
idx_pk
Out[91]: array([8, 1, 7, 0, 2, 3, 4, 5, 6])

idx_val = val[idx_pk]
idx_val
Out[93]: array([  2.1 ,   3.75,   7.2 ,  15.5 , 142.88, 142.88,    nan,    nan,   nan], dtype=float32)

根據 JV 的定義,還有一些步驟可以得到二級索引表示。 德席爾瓦等人。

  1. 擺脫nan
  2. 計算二級索引的唯一值
  3. 對於每個唯一值,計算包含該值的表中所有行的主鍵索引列表

具有鄰接表的唯一二級索引

def secondary_index_with_adjacency_list(arr):
    idx_pk = np.argsort(arr)
    idx_val = arr[idx_pk]
    cnt = np.count_nonzero(~np.isnan(idx_val))
    usec_ndx, split_ndx, cnt_arr = np.unique(idx_val[:cnt], return_index=True, return_counts=True)
    adj_list = np.split(idx_pk[:cnt], split_ndx)[1:]

    return usec_ndx, cnt_arr, adj_list

ndx, freq, adj = secondary_index_with_adjacency_list(val)

pd.DataFrame({'val': ndx, 'freq': freq, 'adj': adj})

Out[11]: 
      val  freq     adj
0    2.10     1     [8]
1    3.75     1     [1]
2    7.20     1     [7]
3   15.50     1     [0]
4  142.88     2  [2, 3]

討論

在實踐中,使用具有重復值的二級索引表示比使用指向表記錄的指針列表的表示更快,但第二個具有更接近我在TRIADB 中使用的超圖表示的有趣屬性。

此解決方案中描述的二級索引更適合分析、過濾不適合內存但以列存儲格式存儲在磁盤上的大數據集。 在這種情況下,對於一組特定的列,可以重建內存(列存儲)格式的記錄子集,甚至可以將其呈現在超圖上(請繼續關注下一版本的 TRIADB)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM