python - 格式化数据的快速查询

标签 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列),因此效率变得至关重要。

有 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', '1

      ind = np.logical_and(np.logical_and(A['var2']=='b1', 1<A['var4']<3), 0<A['var5']<3)
      

SQL 可能是个好方法,但使用数据库来完成这个小任务看起来太重了。而且我无权在任何地方安装数据库支持。

对于快速内存查询的数据结构有什么建议吗? (如果很难有一个简单的自定义实现,sqlitepandas.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 转换为表创建语句的函数。

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

我已经得到了使用 python3pipermail 示例。测试示例有 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/

相关文章:

python - 使用刷新 token 刷新谷歌访问 token

python - 为什么我在 SSH 服务器上安装 SpaCy en_core_web_sm 时遇到 PermissionError

php - Codeigniter,插入 ID

sql - 获取有条件的数据

sql - dbvisualizer:在选择查询中设置最大行数

python - 使用 numpy 保存带有预定义分隔符的按行 txt

python - 如何只保留数据框列中的字符串

Python 嵌套生成器不工作

python - Numba 的最佳可能位数组

python-3.x - 使用随机整数和跨列的有限总和创建 pandas 数据框