在我的程序中,我需要通过元数据进行查询。
我从类似 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列),因此效率变得至关重要。
有 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')
可以看到,每次我们将列的所有元素与给定字符串进行比较。
我认为映射可能是一种更有效的索引方式,所以我将 recarray A
转换为字典 D = {'var1_var2_var3
: [var4, var5, var6 ], ..., 并通过
fnmatch(keys, pat)` 搜索键。我不确定这是更好的方法。
或者我可以制作一个分层字典 {'var1':{'var2':{'var3':[],...},...},...}
或内存中的 hdf5 /var1/var2/var3
并尝试获取该项目(如果存在)。这看起来是最快的方法?
后两种查询不是很频繁,我可以接受numpy recarray比较的方式。
获取特定范围内所有行后面各列的数值,例如
获取行 '1
ind = np.logical_and(1<A['var4']<3), 0<A['var5']<3)
以上两者的结合,例如,
获取行
var2='b1'
, '1ind = np.logical_and(np.logical_and(A['var2']=='b1', 1<A['var4']<3), 0<A['var5']<3)
SQL
可能是个好方法,但使用数据库来完成这个小任务看起来太重了。而且我无权在任何地方安装数据库支持。
对于快速内存查询的数据结构有什么建议吗? (如果很难有一个简单的自定义实现,sqlite
和 pandas.dateframe
似乎是可能的解决方案,如建议的那样。)
最佳答案
使用您的文件示例('b' 代表 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
数组中,每一列都有不同的数据类型(如果列中的类型不同,则为对象数据类型)。但是我还没有看到关于 pandas
速度和速度技巧的讨论。除非它提供某种索引,否则我不希望速度有太大提高。
我可以想象将这些数据写入数据库。 sqlite3
是内置的并且具有内存
模式,因此您不需要文件访问。但是我对那些我将继续演示它的代码完全没有实践。我也不知道执行此类查询有多容易或多快。
https://mail.scipy.org/pipermail/scipy-user/2007-August/013350.html有一些代码可以将结构化数组保存到 sqlite3 数据库中。它包含一个将 dtype
转换为表创建语句的函数。
====================
我已经得到了使用 python3
的 pipermail
示例。测试示例有 11 个字段。有 5000 条记录,
data[np.where(data['id']=='id2000')]
比相应的 sqlite3
查询快 6 倍(使用现有的 cursor
):
cursor.execute('select * from data where id=?',('id2000',))
cursor.fetchone()
关于python - 格式化数据的快速查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38798033/