通过多个键对字典列表进行分组和汇总

时间:2019-01-18 06:45:07

标签: python python-3.x list dictionary unique

我有一个包含字典(List[Dict, Dict, ...])的列表,我想根据两个键对列表进行唯一化,但是我想保留字典中另一个键的值以确保不会丢失通过在要保留的密钥中列出一个清单。我正在使用Python编写代码。确切地说,它对Python 3.x有意义。

让我们假设我具有以下带有三个键的字典列表:numberfavoritecolor。我想使用键numberfavorite来唯一化列表元素。但是对于具有相同值numberfavorite的字典,我想在键color下添加一个列表,以确保我拥有所有color numberfavorite的相同组合。此列表也应该是唯一的,因为对于相同的组合,它不需要重复的color。但是,如果最终结果中的键色只有一个元素,则应为字符串而不是列表。

lst = [
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': False, 'color': 'green'},
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': True, 'color': 'red'},
{'number': 2, 'favorite': False, 'color': 'red'}]

使用上述uniqify,我将得到以下结果:

lst = [
    {'number': 1, 'favorite': False, 'color': {'red', 'green'}},
    {'number': 1, 'favorite': True, 'color': 'red'},
    {'number': 2, 'favorite': False, 'color': 'red'},
]

请注意,red只有一个实例,其中number1,而favoriteFalse,即使它在列表中之前出现过两次这是唯一的。另请注意,当第二个字典中的键color仅具有一个元素时,它是一个字符串而不是一个列表。

6 个答案:

答案 0 :(得分:15)

使用纯python,您可以插入OrderedDict中以保留插入顺序:

from collections import OrderedDict

d = OrderedDict()
for l in lst:
    d.setdefault((l['number'], l['favorite']), set()).add(l['color'])

[{'number': k[0], 'favorite': k[1], 'color': v.pop() if len(v) == 1 else v} 
    for k, v in d.items()]   
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': 'red', 'favorite': True, 'number': 1},
#  {'color': 'red', 'favorite': False, 'number': 2}]

这也可以使用熊猫GroupBy API轻松完成:

import pandas as pd

d = (pd.DataFrame(lst)
       .groupby(['number', 'favorite'])
       .color
       .agg(set)
       .reset_index()
       .to_dict('r'))
d
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': {'red'}, 'favorite': True, 'number': 1},
#  {'color': {'red'}, 'favorite': False, 'number': 2}]

如果需要单个元素的字符串条件,则可以使用

[{'color': (lambda v: v.pop() if len(v) == 1 else v)(d_.pop('color')), **d_} 
     for d_ in d]
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': 'red', 'favorite': True, 'number': 1},
#  {'color': 'red', 'favorite': False, 'number': 2}]

答案 1 :(得分:3)

在纯Python中,一个解决方案是将defaultdict与复合键一起使用。您可以使用它来合并您的值。 之后,您可以再次从该词典中创建一个列表。

from collections import defaultdict

dct = defaultdict([])

for entry in lst:
    dct[(entry['number'], entry['favorite'])].append(entry['color'])

lst = [{'number': key[0], 'favorite': key[1], color: value if len(value) > 1 else value[0]}
    for key, value in dct.items()]

答案 2 :(得分:2)

groupby中的itertools

import itertools
lst = [
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': False, 'color': 'green'},
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': True, 'color': 'red'},
{'number': 2, 'favorite': False, 'color': 'red'}]
l=[list(y) for x,y in itertools.groupby(sorted(lst,key=lambda x: (x['number'],x['favorite'])),lambda x: (x['number'],x['favorite']))]
print([{k:(v if k!='color' else list(set([x['color'] for x in i]))) for k,v in i[0].items()} for i in l])

输出:

[{'number': 1, 'favorite': False, 'color': ['green', 'red']}, {'number': 1, 'favorite': True, 'color': ['red']}, {'number': 2, 'favorite': False, 'color': ['red']}]

答案 3 :(得分:1)

您可以使用具有默认set值的有序字典。 1 然后使用(number, favorite)作为键来迭代字典列表。之所以可行,是因为元组是可哈希的,因此可以用作字典键。

使用一致的结构是一个好习惯。因此,不要在单个值上使用字符串,而在多个值上使用集合,请在整个过程中使用集合:

from collections import OrderedDict, defaultdict

class DefaultOrderedDict(OrderedDict):
    def __missing__(self, k):
        self[k] = set()
        return self[k]

d = DefaultOrderedDict()  # Python 3.7+: d = defaultdict(set)

for i in lst:
    d[(i['number'], i['favorite'])].add(i['color'])

res = [{'number': num, 'favorite': fav, 'color': col} for (num, fav), col in d.items()]

print(res)
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': {'red'}, 'favorite': True, 'number': 1},
#  {'color': {'red'}, 'favorite': False, 'number': 2}]

如果您坚持根据颜色数量使用不同的类型,则可以重新定义列表理解以使用三元语句:

res = [{'number': num, 'favorite': fav, 'color': next(iter(col)) if len(col) == 1 else col} \
       for (num, fav), col in d.items()]

print(res)
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': 'red', 'favorite': True, 'number': 1},
#  {'color': 'red', 'favorite': False, 'number': 2}]

1 值得注意的是,在3.7之前的Python版本中,不保证字典按插入顺序排列。使用Python 3.7+,您可以利用插入顺序,只需使用dictdict的子类,例如collections.defaultdict

答案 4 :(得分:0)

这是一种实现方法,

我首先使用元组作为组合键构建了 private void replaceFragment(Fragment fragment, String tag) { Fragment currentFragment = getCurrentFragment(); if (currentFragment != null) getSupportFragmentManager().beginTransaction().remove(currentFragment).commit(); getSupportFragmentManager().beginTransaction().add(R.id.container_main, fragment, tag).commit(); } public Fragment getCurrentFragment() { FragmentManager manager = getSupportFragmentManager(); return manager.findFragmentById(R.id.container_main); } private void popBackStack() { FragmentManager fragmentManager = getSupportFragmentManager(); int total = fragmentManager.getBackStackEntryCount(); for (int i = 0; i < total; i++) { fragmentManager.popBackStack(); } } ,然后从该dict中创建了一个新列表。您可以编写理解以进一步减少行数并对其进行优化,希望对您有所帮助。

dict

输出:

new_dict = {}

for item in lst:
    try: # if already exists then append to the list
        new_dict.get((item['number'], item['favorite']))
        new_dict[(item['number'], item['favorite'])].append(item['color'])
    except KeyError: # if it doesn't then create a new entry to that key
        new_dict[(item['number'], item['favorite'])] = [item['color']]


final_list = []
for k, v in new_dict.items(): # keep appending dicts to our list
    final_list.append({'number': k[0], 'favorite': k[1], 'color':set(v)})

print(final_list)

答案 5 :(得分:0)

我的一个朋友在不使用任何外部库的情况下提供了以下功能来解决此问题:

# Generate Data:
library(data.table)
library(lattice)

dat <- data.table(group = 1:180)
dat <- dat[, list(t = 1:(181 - group)), by = group]
parameters <- abs(rnorm(1))
dat[, y := 1 / (t + parameters) + cumsum(rnorm(180, mean = 0, sd = 0.01)), by = group]

# First 10 Groups Visualization
xyplot(data = dat[group < 10], y ~ t | as.character(group), type = "l")

# Nonlinear Mixed-effect Model
library(nlme)

nonlinearFun <- function(m, n, t) {
  return(1 / (t + m) + n)
}

test_nlme <- nlme(
  data = dat,
  y ~ nonlinearFun(m, n, t),
  fixed =  list(m ~ 1),
  random = n ~ 1 | group,
  start = c(m = parameters)
)

使用此Python函数后,他只是简单地进行了uniqify以获得列表中的唯一结果。但是,它不会将单一颜色保留为字符串,而是仅包含一个元素的列表。