快速查询格式化数据

时间:2016-08-05 21:31:51

标签: python database numpy indexing mapping

在我的程序中,我需要查询元数据。

我从类似csv的文本文件**中将数据读入numpy记录数组A,没有重复行**。

var1|var2|var3|var4|var5|var6
'a1'|'b1'|'c1'|1.2|2.2|3.4
'a1'|'b1'|'c4'|3.2|6.2|3.2
'a2'|''|'c1'|1.4|5.7|3.8
'a2'|'b1'|'c2'|1.2|2.2|3.4
'a3'|''|'c2'|1.2|2.2|3.4
'a1'|'b2'|'c4'|7.2|6.2|3.2
...

数百万行,嵌套循环中的查询最多可达十亿次(大部分与前3列匹配),因此效率变得至关重要。< / p> 有3种类型的查询,第一种是最常见的。

  • 获取与给定字符串匹配的前3列中的一个或多个列的行,例如

    • 匹配var1='a2'var2='b1'

      的记录
       ind = np.logical_and(A['var1']=='a2', A['var2']=='b1')
      
    • 要匹配var1='a2'var2='b1'var3='c1'

      的记录
       ind = np.logical_and(np.logical_and(A['var1']=='a2', A['var2']=='b1'), A['var3']=='c1')
      

正如我们所看到的,每次我们将列的所有元素与给定的字符串进行比较。

我认为映射可能是一种更有效的索引方式,因此我将重新排列A转换为dict D = {'var1_var2_var3:[var4,var5,var6],...} {{1} } fnmatch(keys,pat)`。我不确定这是一种更好的方式。

或者我可以创建分层词典, and search through the keys by或内存中的hdf5 {'var1':{'var2':{'var3':[],...},...},...},并尝试获取该项目(如果存在)。这看起来是最快的方式吗?

后两种类型的查询不是很频繁,我可以接受numpy recarray比较的方式。

  • 获取特定范围内后一列中数值的所有行,例如

    • 获取行&#39; 1

      /var1/var2/var3
  • 上述两者的组合,例如,

    • 获取ind = np.logical_and(1<A['var4']<3), 0<A['var5']<3) ,&#39; 1的行

      var2='b1'

ind = np.logical_and(np.logical_and(A['var2']=='b1', 1<A['var4']<3), 0<A['var5']<3) 可能是一个好方法,但是对于这个小任务来说,使用数据库看起来太沉重了。而且我无权在任何地方安装数据库支持。

对快速内存查询的数据结构有何建议? (如果很难有一个简单的自定义实现,SQLsqlite似乎是可能的解决方案,如建议的那样。)

2 个答案:

答案 0 :(得分:0)

使用Pandas,它是为这样的任务而构建的:

# Import
import pandas as pd

# Read CSV
df = pd.read_csv('/path/to/file.csv')

# Selection criteria
# using `.query` method:
df.query('var1 == "a2" & var3 == "c1"')
df.query('var2 == "b1" & 1 < var4 < 3 & 0 < var5 < 3')
# using indexing:
df[(df['var1'] == 'a2') & (df['var3'] == 'c1')]
df[(df['var2'] == 'b1') & df['var4'].between(1,3) & df['var5'].between(0,3)]
# using `.where` method:
df.where((df['var1'] == 'a2') & (df['var3'] == 'c1'))
df.where(df['var2'] == 'b1') & df['var4'].between(1,3) & df['var5'].between(0,3))

More indexing and selecting information

答案 1 :(得分:0)

使用您的文件样本(&#39; b&#39; for py3)

In [51]: txt=b"""var1|var2|var3|var4|var5|var6
    ...: 'a1'|'b1'|'c1'|1.2|2.2|3.4
    ...: 'a1'|'b1'|'c4'|3.2|6.2|3.2
    ...: 'a2'|''|'c1'|1.4|5.7|3.8
    ...: 'a2'|'b1'|'c2'|1.2|2.2|3.4
    ...: 'a3'|''|'c2'|1.2|2.2|3.4
    ...: 'a1'|'b2'|'c4'|7.2|6.2|3.2"""

简单的阅读让我看到双层引用

data = np.genfromtxt(txt.splitlines(), names=True, delimiter='|', dtype=None)

array([(b"'a1'", b"'b1'", b"'c1'", 1.2, 2.2, 3.4), ...
    dtype=[('var1', 'S4'), ('var2', 'S4'), ('var3', 'S4'), ('var4', '<f8'), ('var5', '<f8'), ('var6', '<f8')])

所以我要定义一个转换器来剥离它们(一个csv读者也可以这样做):

def foo(astr):
    return eval(astr)

In [55]: A = np.genfromtxt(txt.splitlines(), names=True, delimiter='|', dtype='U3,U3,U3,f8,f8,f8', converters={0:foo,1:foo,2:foo})
In [56]: A
Out[56]: 
array([('a1', 'b1', 'c1', 1.2, 2.2, 3.4),
       ('a1', 'b1', 'c4', 3.2, 6.2, 3.2), 
       ('a2', '', 'c1', 1.4, 5.7, 3.8),
       ('a2', 'b1', 'c2', 1.2, 2.2, 3.4), 
       ('a3', '', 'c2', 1.2, 2.2, 3.4),
       ('a1', 'b2', 'c4', 7.2, 6.2, 3.2)], 
      dtype=[('var1', '<U3'), ('var2', '<U3'), ('var3', '<U3'), ('var4', '<f8'), ('var5', '<f8'), ('var6', '<f8')])

我可以编写像

这样的测试
In [57]: (A['var1']=='a2')&(A['var2']=='b1')
Out[57]: array([False, False, False,  True, False, False], dtype=bool)
In [58]: (1<A['var4'])&(A['var4']<3)
Out[58]: array([ True, False,  True,  True,  True, False], dtype=bool)

A的所有记录的测试都是在编译numpy代码中完成的,因此它们不应该那么慢。

此数据也可以被视为2个多列字段

In [59]: dt = np.dtype([('labels', '<U3', (3,)), ('data', '<f8', (3,))])
In [60]: A1 = A.view(dt)
In [61]: A1
Out[61]: 
array([(['a1', 'b1', 'c1'], [1.2, 2.2, 3.4]),
       (['a1', 'b1', 'c4'], [3.2, 6.2, 3.2]),
       (['a2', '', 'c1'], [1.4, 5.7, 3.8]),
       (['a2', 'b1', 'c2'], [1.2, 2.2, 3.4]),
       (['a3', '', 'c2'], [1.2, 2.2, 3.4]),
       (['a1', 'b2', 'c4'], [7.2, 6.2, 3.2])], 
      dtype=[('labels', '<U3', (3,)), ('data', '<f8', (3,))])

或直接加载

A = np.genfromtxt(txt.splitlines(), skip_header=1, delimiter='|', dtype='(3)U3,(3)f8', converters={0:foo,1:foo,2:foo})

然后测试可以写成:

In [64]: (A1['labels'][:,0]=='a1') & (A1['labels'][:,1]=='b2') & ((A1['data']<6).any(axis=1))
Out[64]: array([False, False, False, False, False,  True], dtype=bool)

In [65]: (A1['labels'][:,[0,1]]==['a1','b2']).all(axis=1)
Out[65]: array([False, False, False, False, False,  True], dtype=bool)

有时可能更清楚的是为各列提供自己的ID:

var1 = A1['labels'][:,0]   # or A['var1']
....
(var1=='a1')&(var2='b1')&...

可以保存重复的查询或组合。

我相信pandas将其系列存储在numpy数组中,每列的dtype不同(如果列中的类型不同,则为对象dtype)。但我还没有看到pandas速度和速度技巧的讨论。除非它提供某种索引,否则我不会期望提高速度。

我可以想象将这些数据写入数据库。 sqlite3已内置并具有memory模式,因此您无需文件访问权限。但是,我已经完全没有使用我将通过演示的代码了。我也不了解进行这类查询的容易程度或速度。

https://mail.scipy.org/pipermail/scipy-user/2007-August/013350.html有一些代码可以将结构化数组保存到sqlite3数据库。它包含一个将dtype转换为表创建语句的函数。

====================

我已将pipermail示例与python3一起使用。测试示例有11个字段。有5000条记录,

data[np.where(data['id']=='id2000')]

比相应的sqlite3查询快6倍(现有cursor):

cursor.execute('select * from data where id=?',('id2000',))
cursor.fetchone()