Нахождение самых низких значений в массиве dict, которые также имеют совпадающие атрибуты, возвращая наибольшую группировку - PullRequest
0 голосов
/ 11 ноября 2018

Это достаточно легко сделать с помощью нескольких циклов, но я уверен, что есть гораздо более эффективный способ добиться этого, и я стремлюсь учиться.

Рассмотрим следующий массив dict, который представляет данные, извлеченные из базы данных nosql.

x = [
    {
        "loc" : "alpha",
        "tag" : 1,
        "dist" : 5
    },
    {
        "loc" : "bravo",
        "tag" : 0,
        "dist" : 2
    },
    {
        "loc" : "charlie",
        "tag" : 5,
        "dist" : 50
    },
    {
        "loc" : "delta",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "echo",
        "tag" : 2,
        "dist" : 30
    },
    {
        "loc" : "foxtrot",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "gamma",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "hotel",
        "tag" : 0,
        "dist" : 2
    },
]

Я хотел бы найти все элементы с наименьшим значением 'dist', и если должно быть более одного dict с одинаковым наименьшим значением, я хочу, чтобы группировка по атрибуту 'tag' имела наибольшее количество dicts с то же самое низкое значение.

например, желаемые возвращаемые данные из вышеприведенного будут:

r = [
    {
        "LocationName" : "delta",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "foxtrot",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "gamma",
        "tag" : 4,
        "dist" : 2
    }
]

Резюме: dist: 2 - это наименьшее значение, [bravo, delta, foxtrot, gamma, hotel] - все имеют dist 2, [bravo, hotel] имеют тег: 0 и [delta, foxtrot, gamma] есть тег: 4. Массив dicts [delta, foxtrot, gamma] возвращается, поскольку они имеют больше с тем же совпадающим тегом и самым низким dist.

Я использую Python 3.6.

Спасибо за помощь и интерес!

Ответы [ 2 ]

0 голосов
/ 11 ноября 2018

Вы можете указать key (то есть лямбда-функцию) для max() и min(), которые могут помочь с этим. Для вашего первого теста,

lowest_single_dist = min(x, key=lambda i: i["dist"])

возвращает элемент в x с самым низким значением для "dist". Если вы хотите, чтобы все элементы с этим значением тега, вы можете использовать понимание списка:

lowest_dists = [i for i in x if i["dist"] == lowest_single_dist["dist"]]

Чтобы получить наибольшую группировку, я сначала создал бы набор возможных значений для "tag" в этом подмножестве, а затем проверил, сколько из каждого есть в lowest_dists, а затем взял бы тот, который имеет наибольшее число:

tags = [i["tag"] for i in lowest_dists]              # get a list of just the tags
ct = {t: tags.count(t) for t in set(tags)}           # make a dict of tag:count for each unique tag
max_tag = max(ct, key=lambda x: ct[x])               # find the largest count and get the largest tag
r = [i for i in lowest_dists if i["tag"] == max_tag] # use another list comprehension to get all the max tags

Если вы хотите сократить все это до двух строк, вы можете быть менее питоническими и сделать это:

m = min(x, key=lambda i: (i["dist"], -1 * max([j["tag"] for j in x if j["dist"] == i["dist"]].count(i["tag"])))
r = [i for i in x if i["tag"] == m["tag"] and i["dist"] == m["dist"]]

Это использует тот факт, что вы можете вернуть кортеж в качестве ключа для сортировки, и второе значение кортежа будет проверяться, только если первое равно. Я немного расширю эту первую строку и объясню, что делает каждая часть:

m = min(x, key=lambda i: (
    i["dist"], -1 * max(
        [j["tag"] for j in x if j["dist"] == i["dist"]].count(i["tag"])
    ))
  • Самое глубокое понимание списка генерирует список тегов для всех элементов в x с тем же значением для "dist", что и i
  • Затем возьмите количество тегов, которые совпадают с i
  • Умножьте на -1, чтобы сделать его отрицательным, чтобы min() вел себя правильно
  • составляет кортеж i["dist"] и значение, которое мы только что вычислили (частота i["tag"] в x), и возвращаем его для каждого элемента
  • присваивает m элемент списка, который имеет самое низкое значение для "dist" и самое частое значение для "tag"
  • присваивает r подсписок элементов в x с тем же значением для "dist" и "tag"

Так что в основном тот же процесс, что и выше, но короче, менее эффективен и немного сложнее.

0 голосов
/ 11 ноября 2018

отсортировать список словарей по значению, которое они имеют в 'dist', и взять самое низкое

x.sort(key= lambda x:x['dist'])
lowest = x[0]['dist']

создать список словарей со значением 'dist', равным самому низкому

x2 = [i for i in x if i['dist']==lowest]

Это должен быть ваш ответ. Если в списке более одного элемента, повторите описанную выше процедуру.

if len(x2)>1:
  x3 = [i['tag'] for i in x2]
  mode = max(set(x3), key=x3.count)
  r = [i for i in x if i['tag']==mode]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...