Как отфильтровать строки списка словаря списков по определенному номеру?

У меня есть огромный словарь списков, которые нужно фильтровать. Вот пример его вывода:

d = {
    'hate': [(2310, "Experiencer: 'like hours'", 212, 222),
              (2310, "Experiencer: 'two'", 1035, 1038),
              (2310, "Experiencer: 'Anakin'", 1560, 1566),
              (2310, "Experiencer: ' '", 1619, 1620),
              (2310, "Experiencer: 'Tatooine'", 1726, 1734),
              (2310, "Experiencer: 'Anakin'", 1775, 1781),
              (2310, "Experiencer: 'Master Qui-Gon'", 1863, 1877),
              (2310, "Experiencer: 'half'", 1883, 1887),
              (2310, "Experiencer: 'One'", 2114, 2117),
              (2310, "Experiencer: 'Anakin'", 2180, 2186),
              (2310, "Stimulus: 'One'", 2484, 2487),
              (2310, "Stimulus: 'Anakin'", 2564, 2570),
              (2310, "Stimulus: 'Padme'", 2739, 2744)],
'confirmation': [(4132, "Experiencer: 'like hours'", 212, 222),
              (4132, "Experiencer: 'two'", 1035, 1038),
              (4132, "Experiencer: 'Anakin'", 1560, 1566),
              (4132, "Experiencer: ' '", 1619, 1620),
              (4132, "Experiencer: 'Tatooine'", 1726, 1734),
              (4132, "Experiencer: 'Anakin'", 1775, 1781),
              (4132, "Experiencer: 'Master Qui-Gon'", 1863, 1877),
              (4132, "Experiencer: 'half'", 1883, 1887),
              (4132, "Experiencer: 'One'", 2114, 2117),
              (4132, "Experiencer: 'Anakin'", 2180, 2186),
              (4132, "Experiencer: 'One'", 2484, 2487),
              (4132, "Experiencer: 'Anakin'", 2564, 2570),
              (4132, "Experiencer: 'Padme'", 2739, 2744),
              (4132, "Experiencer: 'Anakin'", 2782, 2788),
              (4132, "Experiencer: ' '", 2818, 2819),
              (4132, "Experiencer: 'centuries'", 3562, 3571),
              (4132, "Experiencer: 'one'", 3585, 3588),
              (4132, "Experiencer: 'Anakin'", 3679, 3685),
              (4132, "Experiencer: 'Anakin Skywalker'", 3789, 3805),
              (4132, "Experiencer: 'Obi-Wan'", 4014, 4021),
              (4132, "Experiencer: 'Qui-Gon'", 4025, 4032),
              (4132, "Experiencer: 'Qui-Gon's'", 4100, 4109),
              (4132, "Stimulus: 'Anakin'", 4281, 4287),
              (4132, "Stimulus: ' '", 4355, 4356),
              (4132, "Stimulus: 'Anakin'", 4436, 4442)]}

Каждый ключ (один из них - hate, как указано выше) имеет номер в начале каждого элемента списка. Вот он: 2310.

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

Пример вывода:

'hate': [(2310, "Experiencer: 'Anakin'", 2180, 2186),
         (2310, "Stimulus: 'One'", 2484, 2487)]

потому что

(2310, "Experiencer: 'Anakin'", 2180, 2186)

имеет номер 2180, который является следующим наименьшим по сравнению с 2310

и взамен:

(2310, "Stimulus: 'One'", 2484, 2487)

имеет номер 2484, который является следующим по величине по сравнению с 2310

Думаю, для этого нужен шлейф for? Как мне перебрать словарь списков, сравнить первое, повторяющееся число с первыми числами каждой строки и вернуть самые близкие, как упомянуто выше?

Надеюсь, мой вопрос достаточно понятен ...

Заранее спасибо!

РЕДАКТИРОВАТЬ:

Целью было бы автоматизировать процесс просмотра словаря и обновить его, фильтруя его.

Желаемый результат этого словаря будет примерно таким:

 d = {
        'hate': [(2310, "Experiencer: 'Anakin'", 2180, 2186),
                 (2310, "Stimulus: 'One'", 2484, 2487)],
        'confirmation': [(4132, "Experiencer: 'Qui-Gon's'", 4100, 4109),    
                         (4132, "Stimulus: 'Anakin'", 4281, 4287)],
...}

Я также отредактировал приведенный выше пример вывода, который я получил до сих пор. Это словарь списков

плакат не ошибается @JacobIRR - это словарь списков

vasia 09.08.2018 19:22

@vasia, о, я только что понял, что ключ был в этой первой строке.

JacobIRR 09.08.2018 19:22

Вы также должны опубликовать свою попытку решить проблему.

Austin 09.08.2018 19:24

Представленные здесь данные отсортированы по первому изменяющемуся числу. Так всегда бывает?

Patrick Haugh 09.08.2018 19:32

@Austin До сих пор я пробовал другой подход, пытаясь разделить две разные метки на два разных словаря, но это все еще работа.

Waldkamel 09.08.2018 19:40

@patrickhaugh да, это индекс ключа, здесь hate. Каждый ключ имеет уникальный индекс, который повторяется в списке как первое значение. Итак, сравнение этого значения в каждом ключе с первым числом в «кортеже» в конце строки, которое на самом деле является смещением символа, дало бы мне следующее наименьшее и следующее наибольшее. Я заметил закономерность: каждый раз, когда список Experiencer заканчивается и начинается список Stimulus, мне всегда нужны эти две строки. Пример в вопросе.

Waldkamel 09.08.2018 19:44

@Waldkamel «варьировать» означает «изменяться». Кажется, что ваши списки уже отсортированы по третьему значению. Это просто случайность для этих данных или так всегда?

Patrick Haugh 09.08.2018 19:46

@PatrickHaugh ах, да - действительно: он меняется или меняется, когда предоставляется новый ключ - я могу опубликовать другой пример, если хотите. В функции, создавшей этот словарь, говорится: если третьи значения меньше индекса, пометьте их как Experiencer, а если больше, то пометьте как Stimulus - вот почему они уже отсортированы. Но я не могу понять, как просто указать точки встречи в словаре и игнорировать все остальное.

Waldkamel 09.08.2018 19:49
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
8
78
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Возможно, самый простой способ -

  • Отсортируйте список по интересующему элементу, позиция 2.
  • Используйте двоичный поиск, чтобы найти, где 2310 появится в этом списке; вы должны быть между двумя элементами. Это те два элемента, которые вам нужны.

Используйте itertools.groupby, чтобы сгруппировать все похожие элементы из каждого списка, а затем отсортировать их (на основе абсолютной разницы) и получить первые 2 элемента.

>>> from itertools import groupby
>>> 
>>> f = lambda t: t[0]
>>> {key:sorted(v, key=lambda t: abs(k-t[3]))[:2] for key,lst in d.items() for k,v in groupby(sorted(lst, key=f), f)}
{'confirmation': [(4132, "Experiencer: 'Qui-Gon's'", 4100, 4109), (4132, "Experiencer: 'Qui-Gon'", 4025, 4032)], 'hate': [(2310, "Experiencer: 'Anakin'", 2180, 2186), (2310, "Stimulus: 'One'", 2484, 2487)]}

Думаю, здесь стоит остановиться на выборе key, который вы используете для сортировки. Также здесь не определен f.

pault 09.08.2018 19:32

@pault. Спасибо .. Обновил ответ.

Sunitha 09.08.2018 19:52

@Sunitha Я отредактировал свой вопрос для ясности. Я хотел бы просмотреть весь словарь, ключ за ключом и отфильтровать их отсортированные списки в зависимости от первого уникального числа, которое каким-то образом представляет ключ.

Waldkamel 09.08.2018 20:22

Это самый простой и ванильный способ сделать это (из всех, что я мог придумать). Ниже приведено решение, использующее itertools, более элегантное, но более сложное для понимания новичком.

Если l - это список, на который указывает hate в вашем словаре:

target_num = l[0][0]
closest_smaller, closest_bigger = 0,0
closest_smaller_diff, closest_bigger_diff = float("inf"), float("-inf")
for element in l:
    for num in (l[-2],l[-1]):
        diff = target_num - num
        if diff > 0 and diff < closest_smaller_diff:
            closest_smaller = num
            closest_smaller_diff = diff
        if diff < 0 and diff > closest_bigger_diff:
            closest_bigger = num
            closest_bigger_diff = diff
print(closest_smaller, closest_bigger)
# let big_dict be the big list you start with
output_dict = {}
for key, value in big_dict.items(): 
    # break the list into two lists, for those with third value greater
    # and those with third value lesser/equal
    higher_tuples = [i for i in value if i[2] > i[0]]
    lower_tuples = [i for i in value if i[2] <= i[0]]
    # Get the values from that list with 
    high_closest = min(higher_tuples, key=lambda x: x[2] - x[0])
    low_closest = min(lower_tuples, key=lambda x: x[0] - x[2])
    # bind them into an output
    output_duct[key] = [high_closest, low_closest]

Если вы хотите, вы можете связать все это в один действительно большой однострочный файл:

output_dict = {key: [min([i for i in value if i[2] > i[0]], key=lambda a: a[2] - a[0]), min([j for j in value if j[0] <= j[2]], key=lambda b: b[0] - b[2])] for key, value in big_dict.items()}

Если вы собираетесь выполнить два сканирования по списку, почему бы не min((x for x in value if x[2] > x[0]), key=lambda n: abs(n[3] - n[0])), и то же самое для x[2] <= x[0]?

Patrick Haugh 09.08.2018 19:35

на самом деле ты абсолютно прав. Я изменю это

Green Cloak Guy 09.08.2018 19:36

Ваш ответ на самом деле наиболее близок к тому, что я ищу. Единственное, он каким-то образом выводит только две метки из Stimulus-метки: актуальная строка Stimulus, которую я ищу, но еще одна случайная строка Stimulus. Пример вывода вашего кода в пример в моем вопросе: { 'hate': [(2310, "Stimulus: 'One'", 2484, 2487), (2310, "Stimulus: 'Anakin'", 8802, 8808)], 'confirmation': [(4132, "Stimulus: 'Anakin'", 4281, 4287), (4132, "Stimulus: 'Anakin'", 8802, 8808)],}

Waldkamel 09.08.2018 20:33
Ответ принят как подходящий

Если ваши списки уже отсортированы, мы можем использовать bisect, чтобы найти место между записями «Опытный пользователь» и «Статус»:

from bisect import bisect

l=[(2310, "Experiencer: 'like hours'", 212, 222), (2310, "Experiencer: 'two'", 1035,1038), (2310, "Experiencer: 'Anakin'", 1560, 1566), (2310, "Experiencer: ' '", 1619, 1620), (2310, "Experiencer: 'Tatooine'", 1726, 1734), (2310, "Experiencer: 'Anakin'", 1775, 1781), (2310, "Experiencer: 'Master Qui-Gon'", 1863, 1877), (2310, "Experiencer: 'half'", 1883, 1887), (2310, "Experiencer: 'One'", 2114, 2117), (2310, "Experiencer: 'Anakin'", 2180, 2186), (2310, "Stimulus: 'One'", 2484, 2487), (2310, "Stimulus: 'Anakin'", 2564, 2570), (2310, "Stimulus: 'Padme'", 2739, 2744)]

right_index = bisect(l, (2310, "F"))  # "F" comes between "Experiencer" and "Status" 
lower, higher = l[right_index-1], l[right_index]

print(lower, higher, sep = "\n")

# (2310, "Experiencer: 'Anakin'", 2180, 2186)
# (2310, "Stimulus: 'One'", 2484, 2487)

Тогда вы можете легко обработать свой словарь

from bisect import bisect

def get_boundary(l):  # This assumes all lists in your dict have at least 2 items
    if len(l) < 2:
        return l
    right_index = bisect(l, (l[0][0], "F"))  
    return [l[right_index-1], l[right_index]]

print({key: get_boundary(value) for key, value in d.items()})

производит

{'hate': [(2310, "Experiencer: 'Anakin'", 2180, 2186), 
          (2310, "Stimulus: 'One'", 2484, 2487)], 
 'confirmation': [(4132, "Experiencer: 'Qui-Gon's'", 4100, 4109), 
                  (4132, "Stimulus: 'Anakin'", 4281, 4287)]
}

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

Waldkamel 09.08.2018 20:23

@Waldkamel Я почти уверен, что это именно то, что вам нужно. Это должно быть очень быстро, так как при этом не создаются какие-либо промежуточные продукты памяти, а для просмотра списков используется двоичный поиск.

Patrick Haugh 09.08.2018 20:33

Я получаю для некоторых словарей других файлов IndexError: list index out of range - по-видимому, есть списки, в которых есть только один элемент. Есть ли способ решить эту проблему?

Waldkamel 09.08.2018 23:54

Вы можете проверить длину списка в get_boundary.

Patrick Haugh 09.08.2018 23:56

Я так и сделал, но, к сожалению, по-прежнему отображается то же сообщение об ошибке. Может ли быть, что есть даже списки без элементов, которые могут привести к этому сообщению об ошибке?

Waldkamel 10.08.2018 00:06

Я отредактировал свой ответ, чтобы учесть это. Может ли быть какой-то список, который не соответствует шаблону Experiencer ... Stimulus?

Patrick Haugh 10.08.2018 00:08

Snap, я только что заметил, просматривая словарь вручную, что некоторые ключи делать имеют переменный индекс, поскольку они несколько раз появляются в тексте и добавляются к одному и тому же ключу. Это должно быть проблемой, верно? Таким образом, с каждым изменением индекса ключа цикл для Experiencer и Stimulus начинается снова. Итак, проблема не в длине списка.

Waldkamel 10.08.2018 00:15

Тем не менее, он работал с другими словарями, в которых было то же самое, но он рассматривал только первый индекс и игнорировал остальные, независимо от того, сколько следовало за одним ключом. Хм ... так что это могло быть?

Waldkamel 10.08.2018 00:58

Другие вопросы по теме