1

我一直在试验一个应用程序,我遇到了这个问题。我有一个规则列表,如下所示。这是实验数据,真实数据有更多字段(30+)。每条记录都可以包含一些值和一些空值。这是一个列表列表,但我也可以将它保存在 defaultdict 中(如果有帮助的话)。大约 100 万条记录。

Age  Gender  City    Religion  Propensity
23   *       Delhi   *         0.33
*    M       Mumbai  *         0.78
*    *       *       Hindu     0.23
34   F       Chennai *         0.33
...
...
...

现在我有一个数据集 - (23, M, Delhi, Hindu),它包含所有值。

我需要从上表中选择与该记录匹配的所有记录,即使一个维度以尽可能快的速度按维度数降序排列。所以在这种情况下,第 3 行和第 1 行匹配。因此,空值数量最少的记录将位于底部。

我需要一种复杂的方法来实现这一点,它可以在 python 中大规模工作。不能用其他软件。

4

4 回答 4

0

假设您的“搜索条件”始终相同,即“数据集” (年龄、性别、城市、宗教)

将其移动到由“数据集”索引的列表(或集合)字典中

result_dict = {}
for record in record_list:
    # you have to know the indexes
    # I'm assuming 0=Age, 1=Gender, 2=City, 3=Hindu
    key_data = []
    for index in [0, 1, 2, 3]:
        key_data.append(str(record[index]))
    key = ','.join(key_data)
    try:
        result_dict[key].append(record)
    except KeyError:
        result_dict[key] = []
        result_dict[key].append(record)

# Find all records that match '23,M,Delhi,Hindu'
print result_dict['23,M,Delhi,Hindu']

但实际上,我可能会将其存储在数据库中,然后在其上运行 SQL 查询。

于 2012-12-06T12:09:26.940 回答
0

这里有几个很好的答案。我已经编写并测试了一些代码。

首先,这里是对需求的简单实现:

import pprint

t = [
    [ 23,   None, 'Delhi',   None,    0.33 ],
    [ None, 'M',  'Mumbai',  None,    0.78 ],
    [ None, None,  None,     'Hindu', 0.23 ],
    [ 34,   'F',  'Chennai', None,    0.33 ],
]

rlen = len(t[0])

# None may require special handling
m = [23, 'M', 'Delhi', 'Hindu', None]

a = [[] for i in range(rlen+1)]

for r in t:
    s = sum([1 for i in range(rlen) if r[i] == m[i]])
    if 0 < s:
        a[s].append(r)

# Print rows from least matching to most matching (easy to reverse)
rtable = [row for n in a for row in n]
pprint.pprint(rtable)

问题是我们扫描每一行并检查每个元素值。为了避免在最后进行排序,我们为每个可能的匹配计数保留单独的列表,然后我们将列表列表展平以获得最终结果。我希望这相对于表的大小执行大约 O(n),如果我们有大量匹配项(构建大型结果列表将比 O(n) 慢,接近 O(n^2) 作为最坏的情况下)。

如果我们索引表,我们可以加快速度。我们可以每列使用一个 dict 并使用集合组合列。

from collections import defaultdict
import pprint

# data table
TABLE = [
    [ 23,   None, 'Delhi',   None,    0.33 ],
    [ None, 'M',  'Mumbai',  None,    0.78 ],
    [ None, None,  None,     'Hindu', 0.23 ],
    [ 34,   'F',  'Chennai', None,    0.33 ],
]

# The index is a list of dicts, cdictlist.
# cdictlist is indexed by column number to get the column dict.
# The column dict's key is the data value of the column
def BuildIndex(table):
    rlen = len(table[0])
    rrange = range(rlen)
    cdictlist = [defaultdict(set) for i in range(rlen+1)]
    for ir in range(len(table)):
        r = table[ir]
        for ic in rrange:
            f = r[ic]
            cdictlist[ic][f].add(ir)
    return cdictlist


def multisearch(table, match, cdictlist):
    # rcounts is row counts, number of times columns have matched for a row
    rccounts = defaultdict(int)

    #rset is the result set, set of row indexes returned for this search
    rset = set()

    for ic in range(len(table[0])):
        cset = cdictlist[ic].get(match[ic], set())
        rset = rset.union(cset)
        for i in cset:
            rccounts[i] += 1

    # sort the list by column match count, original row index
    l = sorted((v,k) for (k,v) in rccounts.iteritems())

    # return list of rows, for each row we return (count, index, raw data)
    lr = [ [l[i][0], l[i][1]] + table[l[i][1]] for i in range(len(l)) ]
    return lr

def main():
    cdictlist = BuildIndex(TABLE)

    # None may require special handling
    match = [23, 'M', 'Delhi', 'Hindu', None]

    lr = multisearch(TABLE, match, cdictlist)
    pprint.pprint(lr)

if __name__ == '__main__':
    main()

性能将取决于返回的记录数而不是表的大小。对于大量匹配,集合并集操作将很快成为问题。如果任何字段匹配并且示例字段之一是 Gender,则记录匹配,因此我们应该期望至少返回一半的行。

如果您必须匹配所有列,这种方法会更好。我们可以通过构建未返回的记录集(使用集合交集)来改进这一点然后过滤掉这些记录。

于 2012-12-06T15:20:28.200 回答
0

假设您确实使用了您在评论中提到的 SQL 数据库 (sqlite3),则 SQL 将如下所示:

-- gives you the set of complete records
SELECT
    v0.*
FROM values v0
WHERE -- only full records
    v0.Age IS NOT NULL
AND v0.Gender IS NOT NULL
AND v0.City IS NOT NULL
AND v0.Religion IS NOT NULL
AND v0.Propensity IS NOT NULL


SELECT v1.*, 
    CASE v1.Age WHEN IS NULL THEN 0 ELSE 1 END + 
    CASE v1.Gender WHEN IS NULL THEN 0 ELSE 1 END +
    CASE v1.City WHEN IS NULL THEN 0 ELSE 1 END +
    CASE v1.Religion WHEN IS NULL THEN 0 ELSE 1 END + 
    CASE v1.Propensity WHEN IS NULL THEN 0 ELSE 1 END as dimensions
FROM values v1
WHERE v1.Age = 23
OR    v1.Gender = 'M'
OR    v1.City = 'Delhi'
OR    v1.Religion = 'Hindu'
ORDER BY dimensions desc
于 2012-12-06T12:29:41.480 回答
0

您可以将数据存储在一组字典中:

dict1:age->list<entry>
dict2:gender->list<entry>
...

现在,当您收到查询时 - 您所要做的就是创建一个直方图(map:entry->integer) 根据值对其进行排序并按降序打印。

运行时间是O(d*m + mlogm)(平均),其中d是列表(维度) m的数量,是输出条目的数量。

伪代码:

assume  list of dictionaries, let it be L:
printRelevants(entry):
   histogram <- new dictionary
   for each dimension d:
      l <- L[d].get(entry[d])
      for each element e in l: #remember to check for null l first
         val <- histogram.get(e)
         if val is null:
             histogram.put(e,1)
         else:
             histogram.put(e,val+1) #assuming overriding old entry with the same key
    #done creating the histogram! 
    sort histogram according to value
    print keys of histogram in descending order
于 2012-12-06T11:56:54.247 回答