【问题标题】:Fast query in formatted data格式化数据中的快速查询
【发布时间】:2016-12-12 09:19:36
【问题描述】:

在我的程序中,我需要通过元数据进行查询。

我从类似 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 转换为 dict D = {'var1_var2_var3: [var4, var5, var6], ...}, and search through the keys byfnmatch(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 可能是一个好方法,但是对于这个小任务使用数据库看起来太重了。而且我无权在任何地方安装数据库支持。

对于快速内存查询的数据结构有什么建议吗? (如果很难有一个简单的自定义实现,sqlite 和 pandas.dateframe 似乎是可能的解决方案,正如建议的那样。)

【问题讨论】:

  • 解释你对numpy效率的担忧更多?至于数据库解决方案,sqlite3 可以在内存中使用。
  • @hpaulj 每个查询都需要将数百万个项目与给定值进行比较,这看起来很浪费。我曾经尝试使用dict,但似乎正则表达式匹配并不快。如果为此目的很难有一个简单的自定义 python 代码,sqlite 和 pandas.dateframe 似乎是可能的解决方案。
  • 在numpy中,双边测试必须写成(1&lt;A['var4'])&amp;(A['var4']&lt;3)

标签: python database numpy indexing mapping


【解决方案1】:

使用您的文件示例(py3 的“b”)

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(如果类型在一列内变化,则为 object 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()

【讨论】:

    【解决方案2】:

    使用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

    【讨论】:

    • 我猜pandas.dataframe中的查询和后台的numpy.recarray比较是一样的吧?还是使用一些技巧对其进行了优化?
    • pandas 倾向于对列使用numpy 数组,尽管 dtype 可能不是结构化的 (recarray)。对于加载 CSV 等任务,它确实有更快的代码,但我怀疑这样的任务是否存在很大的速度差异。但您可能只需要自己进行速度测试。
    • @Lee,Pandas 使用numexpr 解析查询,据称这在这些事情上非常有效。就像@hpaulj 说的,你必须自己做测试。最棒的是,您可以根据自己的条件创建蒙版。因此,如果您有一个想要索引的固定组合,您可以简单地创建一个掩码,并在您的程序中重用它。例如:mask = (df['var1'] == 'a2') &amp; (df['var3'] == 'c1') 是一个掩码,您可以使用它来索引数据框,如下所示:df[mask]。您可能会进行一次比较,然后重复使用结果。
    • 进一步,@Lee,您甚至可能不需要在 pandas 中进行屏蔽和索引。有groupby 方法,例如可以根据不同的标准对数据进行分组,并对每个组的成员应用任意数量的函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多