熊猫中的快速稀疏向量加法

时间:2014-05-06 23:22:02

标签: python vector pandas

这是一个关于如何进行大量表连接以便在Pandas中进行矢量数学运算的问题。

因此,通过一个非常非常长的处理链,我将大量表示为HDF5表的数据放入一组约20个稀疏向量中,表示为带有基于字符串的MultiIndexes的Pandas DataFrames。这些向量所在的空间非常复杂且高维(它的自然语言数据),但它们有些重叠。维度本身具有层次结构(因此MultiIndex)。假设它们各自具有大约5K-60K的尺寸,并且重叠尺寸的总数(可以根据20 I调用而不同)大约为200K。 (FULL空间的FAR超过200K!)

到目前为止它非常快,一次性将表格处理成正确的向量。

但是现在我想对齐并总结这些向量。我发现的所有解决方案都相当缓慢。我在Python 2.7上使用Pandas 0.12.0。

A成为我从中获取向量的存储/磁盘。

In [106]: nounlist = ["fish-n", "bird-n", "ship-n", "terror-n", "daughter-n", "harm-n", "growth-n", "reception-n", "antenna-n", "bank-n", "friend-n", "city-n", "woman-n", "weapon-n", "politician-n", "money-n", "greed-n", "law-n", "sympathy-n", "wound-n"]

In [107]: matrices = [A[x] for x in nounlist]

matrices有点误导我在事后认识到。除了MultiIndex,它们只是一个列。)

到目前为止一切顺利。但现在我想加入他们,以便我可以总结一下:

In [108]: %timeit matrices[0].join(matrices[1:], how="outer")
1 loops, best of 3: 18.2 s per loop

这是一个相对较新的处理器(2.7 GHz AMD Opteron)。对于在语音处理系统中理想地使用(在高维度上)的东西来说,它太慢了。

reduce我的运气好一点:

In [109]: %timeit reduce(lambda x, y: x.join(y, how="outer"), matrices[1:], matrices[0])
1 loops, best of 3: 10.8 s per loop

这些在整个运行中保持相当一致。一旦它返回,总和就达到了可接受的速度:

In [112]: vec = reduce(lambda x, y: x.join(y, how="outer"), matrices[1:], matrices[0])

In [113]: %timeit vec.T.sum()
1 loops, best of 3: 262 ms per loop

我最接近合理的时间是:

def dictcutter(mlist):
    rlist = [x.to_dict()[x.columns[0]] for x in mlist]
    mdict = {}
    for r in rlist:
        for item in r:
            mdict[item] = mdict.get(item, 0.0) + r[item]
    index = pd.MultiIndex.from_tuples(mdict.keys())
    return pd.DataFrame(mdict.values(), index=index)

这就像:

In [114]: %timeit dictcutter(matrices)
1 loops, best of 3: 3.13 s per loop

但每一秒都很重要!有没有办法进一步削减它?是否有更智能的方法按维度添加这些向量?

编辑添加 Jeff在评论中要求的详细信息:

关于" fish-n"的矢量的一些细节:

In [14]: vector = A['fish-n']

In [15]: vector.head()
Out[15]: 
                   fish-n
link   word1             
A2     give-v  140.954675
A4     go-v    256.313976
AM-CAU go-v      0.916041
AM-DIR go-v     29.022072
AM-MNR go-v     21.941577

In [16]: vector.info()
<class 'pandas.core.frame.DataFrame'>
MultiIndex: 5424 entries, (A2, give-v) to (A1, gotta-v)
Data columns (total 1 columns):
fish-n    5424  non-null values
dtypes: float64(1)

深入钻探:

In [17]: vector.loc['A0']
Out[17]: 
<class 'pandas.core.frame.DataFrame'>
Index: 1058 entries, isolate-v to overdo-v
Data columns (total 1 columns):
fish-n    1058  non-null values
dtypes: float64(1)

In [18]: vector.loc['A0'][500:520]
Out[18]: 
                 fish-n
word1                  
whip-v         3.907307
fake-v         0.117985
sip-v          0.579624
impregnate-v   0.885079
flavor-v       5.583664
inspire-v      2.251709
pepper-v       0.967941
overrun-v      1.435597
clutch-v       0.140110
intercept-v   20.513823
refined-v      0.738980
gut-v          7.570856
ascend-v      12.686698
submerge-v     1.761342
catapult-v     0.577075
cleaning-v     1.492284
floating-v     5.318519
incline-v      2.270102
plummet-v      0.243116
propel-v       3.957041

现在乘以20,然后尝试将它们全部加起来......

1 个答案:

答案 0 :(得分:0)

创建一些测试数据

In [66]: def mklbl(prefix,n):
   ....:         return ["%s%s" % (prefix,i)  for i in range(n)]
   ....: 

In [67]: mi_total = pd.MultiIndex.from_product([mklbl('A',1000),mklbl('B',200)])

# note that these are random consecutive slices; that's just for illustration
In [68]: ms = [ pd.Series(1,index=mi_total.take(np.arange(50000)+np.random.randint(0,150000,size=1))) for i in range(20) ]

In [69]: ms[0]
Out[69]: 
A417  B112    1
      B113    1
      B114    1
      B115    1
      B116    1
      B117    1
      B118    1
      B119    1
      B120    1
      B121    1
      B122    1
      B123    1
      B124    1
      B125    1
      B126    1
...
A667  B97     1
      B98     1
      B99     1
      B100    1
      B101    1
      B102    1
      B103    1
      B104    1
      B105    1
      B106    1
      B107    1
      B108    1
      B109    1
      B110    1
      B111    1
Length: 50000, dtype: int64

将所有内容推入一个非常长的系列,转换为一个框架(具有相同的索引,此时重复),然后总结索引级别(重复数据删除)

这相当于concat(ms).groupby(level=[0,1]).sum()。 (最后的sort仅用于说明而非必要)。虽然如果你在之后进行任何类型的索引,你可能希望sortlevel()对索引进行排序。

 In [103]: concat(ms).to_frame(name='value').sum(level=[0,1]).sort('value',ascending=False)
Out[103]: 
           value
A596 B109     14
A598 B120     14
     B108     14
     B109     14
     B11      14
     B110     14
     B111     14
     B112     14
     B113     14
     B114     14
     B115     14
     B116     14
     B117     14
     B118     14
     B119     14
     B12      14
     B121     14
     B106     14
     B122     14
     B123     14
     B124     14
     B125     14
     B126     14
     B127     14
     B128     14
     B129     14
     B13      14
     B130     14
     B131     14
     B132     14
     B133     14
     B134     14
     B107     14
     B105     14
     B136     14
A597 B91      14
     B79      14
     B8       14
     B80      14
     B81      14
     B82      14
     B83      14
     B84      14
     B85      14
     B86      14
     B87      14
     B88      14
     B89      14
     B9       14
     B90      14
     B92      14
A598 B104     14
A597 B93      14
     B94      14
     B95      14
     B96      14
     B97      14
     B98      14
     B99      14
A598 B0       14
             ...

[180558 rows x 1 columns]

现在很快

In [104]: %timeit concat(ms).to_frame(name='value').sum(level=[0,1]).sort('value',ascending=False)
1 loops, best of 3: 342 ms per loop