我有这样的数据结构:
items = [
['Schools', '', '', '32'],
['Schools', 'Primary schools', '', '16'],
['Schools', 'Secondary schools', '', '16'],
['Schools', 'Secondary schools', 'Special ed', '8'],
['Schools', 'Secondary schools', 'Non-special ed', '8'],
]
这是一个消费项目清单。有些是聚合,例如items[0]
是所有学校的总支出,items[2]
是中学的总支出。那些不是聚合的是items[1],
items[3]
和items[4]
。
如何优雅地减少列表,使其仅显示非聚合项目?在伪代码中:
for each item in items
check if item[1] is blank, if it is
check if item[0] matches another item’s[0]
if it does and if that item’s[1] isn’t blank
delete item
check if item[2] is blank, if it is
check if item[1] matches another item’s[1]
if it does and if if that item’s[2] isn’t blank
delete item
到目前为止,这是我的(跛脚!)尝试:
for i in range(len(items)):
i -= 1
if items[i]:
if items[i][1] == "":
for other_item in items:
if items[i][0]==other_item[0] and other_item[1]!="":
items_to_remove.append(i)
continue
elif items[i][2]=="":
for other_item in items:
if items[i][1] == other_item[1] and other_item[2] != "":
items_to_remove.append(i)
continue
new_items = [ key for key,_ in groupby(items_to_remove)]
new_items.sort(reverse=True)
for number in new_items:
temp_item = items[number]
items.remove(temp_item)
这太丑了。我能做得更好吗?
注意:我可以使用词典而不是列表,如果这样可以让生活更轻松:)
答案 0 :(得分:2)
首先,我建议您的数据结构应该更像这样:
items = [
['Schools', None, None, 32],
['Schools', 'Primary schools', None, 16],
['Schools', 'Secondary schools', None, 8],
['Schools', 'Secondary schools', 'Special ed', 4],
['Schools', 'Secondary schools', 'Non-special ed', 4],
]
我们可以将它们分类为这样的字典:
result = {}
for item in items:
if not item[0] in result or not isinstance(result[item[0]], dict): result[item[0]] = {}
if not item[1] in result[item[0]] or not isinstance(result[item[0]][item[1]], dict): result[item[0]][item[1]] = {}
if not item[2] in result[item[0]][item[1]] or not isinstance(result[item[0]][item[1]][item[2]], dict): result[item[0]][item[1]][item[2]] = {}
if not item[0]:
result = item[3]
elif not item[1]:
result[item[0]] = item[3]
elif not item[2]:
result[item[0]][item[1]] = item[3]
else:
result[item[0]][item[1]][item[2]] = item[3]
你应该最终得到一个字典:
result = {
'Schools': {
'Secondary schools': {
'Non-special ed': '4',
'Special ed': '4'
},
'Primary schools': '16'
}
}
我的例程可能会被优化并递归。
此外,这些数字总计为24 - 这是您的错误吗?
答案 1 :(得分:1)
list_keys = [ "".join(x[:-1]) for x in items ]
for i in range(len(list_keys)-1):
if not list_keys[i+1].startswith(list_keys[i]):
print items[i]
print items[-1]
在这里,我找到每个项目的“关键字”,它是项目中的所有条目,连接在一起,除了最后一个值。
聚合项目的密钥始终是后续项目密钥的前缀,因此我们可以使用此测试来检测聚合项目并将其解除。
这个alg。打印(在您的输入上):
['Schools', 'Primary schools', '', '16']
['Schools', 'Secondary schools', 'Special ed', '4']
['Schools', 'Secondary schools', 'Non-special ed', '4'],
注意:
这假设所有项目都以树形结构整齐排列(作为原始数据)。如果不是,它会(稍微)更复杂,因为你必须在循环之前对键进行排序(并跟踪哪个键属于哪个项目)。
答案 2 :(得分:0)
如何制作物品对象?
class School (object):
__init__(self, is_aggregate=false):
self.is_aggregate = is_aggregate
答案 3 :(得分:0)
我正在使用
的辅助功能filter
删除聚合项目的函数通过此函数运行示例数据,它将提供所需的输出:)
def remove_aggregates(items):
def mk_pred(index_i, blank_i, items):
posts = set(x[index_i] for x in items if x[blank_i] != '')
def pred(item):
return not (item[blank_i] == '' and item[index_i] in posts)
return pred
items = filter(mk_pred(0,1,items), items)
items = filter(mk_pred(1,2,items), items)
return items
答案 4 :(得分:0)
你已经问过如何优雅地做,以及如何更好地做到这一点。你的nota bene暗示你正在使用的结构仍具有可塑性。如果您希望能够更优雅地这样做,我建议您更改数据的存储方式。一些选项是:
在每个列表中包含一个附加字段,指示它是否为汇总值:
items = [
['Schools', '', '', '32', True],
['Schools', 'Primary schools', '', '16', False],
['Schools', 'Secondary schools', '', '8', True],
['Schools', 'Secondary schools', 'Special ed', '4', False],
['Schools', 'Secondary schools', 'Non-special ed', '4', False],
]
将您的数据拆分为两个列表:
items = [
[
['Schools', '', '', '32'],
['Schools', 'Secondary schools', '', '8'],
],
[
['Schools', 'Primary schools', '', '16'],
['Schools', 'Secondary schools', 'Special ed', '4'],
['Schools', 'Secondary schools', 'Non-special ed', '4'],
],
]
使聚合值包含他们孩子的列表(尽管这仍然不是很有趣):
items = [
['Schools', '', '', '32', [
['Schools', 'Primary schools', '', '16', []],
['Schools', 'Secondary schools', '', '8', [
['Schools', 'Secondary schools', 'Special ed', '4'],
['Schools', 'Secondary schools', 'Non-special ed', '4'],
],
],
]
我会说你数据的当前结构不允许你用它做任何优雅的事情。您需要的逻辑是“如果此索引为空但不是另一个条目在另一个索引上具有相同值”,并且每个列表条目必须执行两次,因为该逻辑可以在两对独立的索引位置。修复存储信息的方式,您将能够编写一种减少数据的优雅方法。
例如,如果您使用我列出的第一个选项(使用布尔值来指示条目是否为聚合),您可以使用以下内容缩小列表:
reduced = [item for item in items where item[4] == False]
答案 5 :(得分:0)
此尝试尝试不依赖于输入的排序:
items = [
['Schools', '', '', '32'],
['Schools', 'Primary schools', '', '16'],
['Schools', 'Secondary schools', '', '16'],
['Schools', 'Secondary schools', 'Special ed', '8'],
['Schools', 'Secondary schools', 'Non-special ed', '8'],
]
def path(item,upto=None):
return ".".join([p for p in item[:-1] if p][:upto])
from collections import defaultdict
children_counter = defaultdict(int)
for i in items:
children_counter[path(i,-1)] += 1
for i in items:
if children_counter[path(i)] == 0:
print i
答案 6 :(得分:-1)
您可以使用列表推导,如下所示:
items = [a for a in items if a[1] != '' and a[2] != '']
或者,如果任何位置的空字符串表示聚合项:
items = [a for a in items if '' not in a]
当然,您不一定需要将简化列表分配给项目 - 您可以随意使用它。