Python - извлечение минимального / максимального значения из списка кортежей - PullRequest
0 голосов
/ 31 октября 2018

У меня есть список кортежей следующим образом:

data = [
    ('A', '59', '62'), ('A', '2', '6'), ('A', '87', '92'),
    ('A', '98', '104'), ('A', '111', '117'),
    ('B', '66', '71'), ('B', '25', '31'), ('B', '34', '40'), ('B', '46', '53'),
    ('B', '245', '251'), ('B', '235', '239'), ('B', '224', '229'), ('B', '135', '140'),
    ('C', '157', '162'), ('C', '203', '208'),
    ('D', '166', '173'), ('D', '176', '183'),
    ('E', '59', '62'), ('E', '2', '6'), ('E', '87', '92'), ('E', '98', '104'), ('E', '111', '117')
]

Они соответствуют подмножеству большего набора данных, поэтому я извлек, как указано выше, чтобы упростить этот пост. Первый элемент каждого кортежа, то есть A, B, C, D, E ... является идентификатором и может присутствовать в нескольких копиях.

Я хотел бы извлечь для каждой ID / категории (A, B, C, D, E ...):

1 - минимум от 2-го элемента кортежа

2 - максимум от 3-го элемента кортежа

Окончательный список вывода должен выглядеть следующим образом:

A: min = 2, max = 117
B: min = 25, max = 251
C: min = 157, max = 208
D: min = 166, max = 183
E: min = 2, max = 117

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

Я упростил тестирование, используя кортежи только с первыми 2 элементами и извлекая только минимум.

Вывод выглядит так:

('A', '111')
('B', '135')
('C', '157')
('D', '166')
('E', '111')

Должно быть:

('A', '2')
('B', '25')
('C', '157')
('D', '166')
('E', '2')

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

Большое спасибо за ваше время.

РЕДАКТИРОВАТЬ 1 - 31/10/2018

Здравствуйте,

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

data_min_only = [('A', '59'), ('A', '2'), ('A', '87'), ('A', '98'), ('A', '111'), ('B', '66'), ('B', '25'), ('B', '34'), ('B', '46'), ('B', '245'), ('B', '235'), ('B', '224'), ('B', '135'), ('C', '157'), ('C', '203'), ('D', '166'), ('D', '176'), ('E', '59'), ('E', '2'), ('E', '87'), ('E', '98'), ('E', '111')]

from collections import OrderedDict

empty_dict = OrderedDict()

for item in data_min_only:

    # Get old value in dictionary if exist
    old = empty_dict.get(item[0])

    # Skip if new item is larger than old
    if old:
        if item[1] > old[1]:
            continue
        else:
            del d[item[0]]

    # Assign
    empty_dict[item[0]] = item

list(empty_dict.values())

Я думал, что проблема заключается в порядке значений кортежей для каждой категории (от наименьшего к наибольшему до итерации по data_min_only.

Спасибо всем авторам за их быстрые ответы и предложения / решения! Сейчас я работаю над этим, чтобы попытаться понять и адаптировать их дальше.

РЕДАКТИРОВАТЬ 2 - 31/10/2018

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

for k, g in groupby(sorted(data), key=lambda x: x[0]):
    vals = [(int(t[1]), int(t[2])) for t in g]
    print (max(i[1] for i in vals) - min(i[0] for i in vals))
    test_lst = []
    test_lst.append((max(i[1] for i in vals) - min(i[0] for i in vals)))

Я тоже пробовал это, но получил тот же результат:

for i in vals:
    test_lst2 = []
    test_lst2.append((max(i[1] for i in vals) - min(i[0] for i in vals)))

Для этого вида цикла, каков наилучший способ извлечь результаты в список?

Еще раз спасибо.

РЕДАКТИРОВАТЬ 3 - 31/10/2018

test_lst = []
for k, g in groupby(sorted(data), key=lambda x: x[0]):
    vals = [(int(t[1]), int(t[2])) for t in g]
    print (max(i[1] for i in vals) - min(i[0] for i in vals))
    test_lst.append((max(i[1] for i in vals) - min(i[0] for i in vals)))

Решение для извлечения данных цикла - пустой список должен быть вне цикла. Пожалуйста, смотрите @slider комментарии для своего поста ниже.

Ответы [ 4 ]

0 голосов
/ 31 октября 2018

Это еще один подход, который будет работать с использованием библиотеки Pandas:

import numpy as np
import pandas as pd

#The same dataset you provided us
data = [('A', '59', '62'), ('A', '2', '6'), ('A', '87', '92'), ('A', '98', '104'), ('A', '111', '117'), ('B', '66', '71'), ('B', '25', '31'), ('B', '34', '40'), ('B', '46', '53'), ('B', '245', '251'), ('B', '235', '239'), ('B', '224', '229'), ('B', '135', '140'), ('C', '157', '162'), ('C', '203', '208'), ('D', '166', '173'), ('D', '176', '183'), ('E', '59', '62'), ('E', '2', '6'), ('E', '87', '92'), ('E', '98', '104'), ('E', '111', '117')]

#Generate dataframe df
df = pd.DataFrame(data=data)
#Convert strings to their respective numerical values
df[[1,2]] = df[[1,2]].apply(pd.to_numeric, errors='ignore')

#Group values using column 0
df.groupby(0).agg({1: min, 2: max})

Мы используем метод agg со словарем в качестве аргумента, чтобы найти минимум в столбце 1 и максимум в столбце 2 для каждого сгруппированного диапазона.

Это дает следующий результат:

     1    2
0
A    2  117
B   25  251
C  157  208
D  166  183
E    2  117
0 голосов
/ 31 октября 2018
data = [('A', '59', '62'), ('A', '2', '6'), ('A', '87', '92'), ('A', '98', '104'), ('A', '111', '117'), ('B', '66', '71'), ('B', '25', '31'), ('B', '34', '40'), ('B', '46', '53'), ('B', '245', '251'), ('B', '235', '239'), ('B', '224', '229'), ('B', '135', '140'), ('C', '157', '162'), ('C', '203', '208'), ('D', '166', '173'), ('D', '176', '183'), ('E', '59', '62'), ('E', '2', '6'), ('E', '87', '92'), ('E', '98', '104'), ('E', '111', '117')]


result = {}  # construct result dictionary
for i in data:
    cur_min, cur_max = map(int, i[1:])
    min_i, max_i = result.setdefault(i[0], [cur_min, cur_max])
    if cur_min < min_i:
        result[i[0]][0] = cur_min
    if cur_max > max_i:
        result[i[0]][1] = cur_max
# print(result)  # dictionary containing keys with list of min and max values for given key >>> {'A': [2, 117], 'B': [25, 251], 'C': [157, 208], 'D': [166, 183], 'E': [2, 117]}

for k, v in result.items():  # loop to print output
    print("{} min: {} max: {}".format(k, v[0], v[1]))

Выход:

A min: 2 max: 117
B min: 25 max: 251
C min: 157 max: 208
D min: 166 max: 183
E min: 2 max: 117
0 голосов
/ 31 октября 2018

Другой подход:

max_list = {}
min_list = {}
for i in data:
    if i[0] not in max_list:
        max_list[i[0]] = -99999
        min_list[i[0]] = 99999

    if max_list[i[0]] < int(i[2]):
        max_list[i[0]] = int(i[2])

    if min_list[i[0]] > int(i[1]):
        min_list[i[0]] = int(i[1])



for ele in max_list:
    print(ele, ' min: ', min_list[ele], 'max: ', max_list[ele])
0 голосов
/ 31 октября 2018

Вы можете использовать itertools.groupby для первой группы с помощью клавиши "id", а затем вычислить минимальное и максимальное значения для каждой группы:

from itertools import groupby

groups = []
for k, g in groupby(sorted(data), key=lambda x: x[0]):
    groups.append(list(g))

for g in groups:
    print(g[0][0], 'min:', min(int(i[1]) for i in g), 'max:', max(int(i[2]) for i in g))

выход

A min: 2 max: 117
B min: 25 max: 251
C min: 157 max: 208
D min: 166 max: 183
E min: 2 max: 117

Обратите внимание, что вам не нужно сначала сохранять группы в списке groups; вы можете напрямую печатать минимальное и максимальное значения при выполнении итерации в цикле groupby for:

for k, g in groupby(sorted(data), key=lambda x: x[0]):
    vals = [(int(t[1]), int(t[2])) for t in g]
    print(k, 'min:', min(i[0] for i in vals), 'max:', max(i[1] for i in vals))
...