Python сортировка списка списков частично обратная на основе даты - PullRequest
5 голосов
/ 22 марта 2020

У меня проблема. У меня есть список списков, которые выглядят примерно так:

[
[datetime.date(2019, 3, 29), Decimal('44819.75')],
[datetime.date(2019, 3, 29), Decimal('45000.00')],
[datetime.date(2019, 3, 28), Decimal('0.00')],
[datetime.date(2019, 3, 22), Decimal('-275.00')],
[datetime.date(2019, 3, 22), Decimal('-350.00')],
[datetime.date(2019, 3, 22), Decimal('-175.00')]
]

Мне нужно, чтобы сортировка была в поле даты (1-й), но каждый набор одинаковых дат должен быть отсортирован в обратном порядке. Результирующий список должен выглядеть следующим образом:

[
[datetime.date(2019, 3, 29), Decimal('45000.00')],
[datetime.date(2019, 3, 29), Decimal('44819.75')],
[datetime.date(2019, 3, 28), Decimal('0.00')],
[datetime.date(2019, 3, 22), Decimal('-175.00')],
[datetime.date(2019, 3, 22), Decimal('-350.00')],
[datetime.date(2019, 3, 22), Decimal('-275.00')],
]

Как видите, список упорядочен по дате, но для тех же дат список перевернут.

даты по-прежнему убывают 2019-3-29 2019-3-28 2019-3-22, но для каждой даты, если существует более 1 элемента на эту дату, элементы меняются местами.

для 2019-3-29 есть 2 элемента

[datetime.date(2019, 3, 29), Decimal('44819.75')],
[datetime.date(2019, 3, 29), Decimal('45000.00')],

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

[datetime.date(2019, 3, 29), Decimal('45000.00')],
[datetime.date(2019, 3, 29), Decimal('44819.75')],

К сожалению, я не могу найти большинство pythoni c способ сделать это, только уродливые вложенные циклы

Ответы [ 7 ]

2 голосов
/ 22 марта 2020

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

# Simplified representation.
# a few random values at the start and then multiple 2's and that the current order is a,b,c
# We expect all values to be sorted on the integer part first. And that the order for the 2's is c,b,a at the end.
data = [
    [1, '-'],
    [5, '-'],
    [3, '-'],

    [2, 'a'],
    [2, 'b'],
    [2, 'c']
]


data = data[::-1]
data = sorted(data, key=lambda x:x[0])

Печать данных даст:

[1, '-']
[2, 'c']
[2, 'b']
[2, 'a']
[3, '-']
[5, '-']

Я уверен, что вы требуемый.

Это решение очень легко прочитать, которое имеет свои преимущества при работе с другими.

sorted в python - это алгоритм стабильной сортировки. Вот почему вы, если вы сортируете нормально, порядок 'ab c' сохраняется. Вот почему реверсирование сначала работает, сортировка не изменит порядок появления одинаковых элементов.

Обратите внимание, что это также работает.

data = sorted(data, key=lambda x:x[0], reverse=True)
data = data[::-1]

Здесь мы делаем обратную сортировку и затем читаем данные в обратном направлении.

2 голосов
/ 22 марта 2020

Решение O (n), использующее itertools.groupby для группировки и реверсирования элементов каждой даты:

data = [d for _, g in groupby(data, lambda d: d[0]) for d in [*g][::-1]]

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

Демонстрация:

import datetime
from decimal import Decimal
from itertools import groupby

data = [
[datetime.date(2019, 3, 29), Decimal('44819.75')],
[datetime.date(2019, 3, 29), Decimal('45000.00')],
[datetime.date(2019, 3, 28), Decimal('0.00')],
[datetime.date(2019, 3, 22), Decimal('-275.00')],
[datetime.date(2019, 3, 22), Decimal('-350.00')],
[datetime.date(2019, 3, 22), Decimal('-175.00')]
]

data = [d for _, g in groupby(data, lambda d: d[0]) for d in [*g][::-1]]

for d in data:
    print(d)

Вывод:

[datetime.date(2019, 3, 29), Decimal('45000.00')]
[datetime.date(2019, 3, 29), Decimal('44819.75')]
[datetime.date(2019, 3, 28), Decimal('0.00')]
[datetime.date(2019, 3, 22), Decimal('-175.00')]
[datetime.date(2019, 3, 22), Decimal('-350.00')]
[datetime.date(2019, 3, 22), Decimal('-275.00')]
1 голос
/ 22 марта 2020

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

data.sort(key=lambda d: d[0])
data.reverse()

Демо:

import datetime
from decimal import Decimal

data = [
[datetime.date(2019, 3, 29), Decimal('44819.75')],
[datetime.date(2019, 3, 29), Decimal('45000.00')],
[datetime.date(2019, 3, 28), Decimal('0.00')],
[datetime.date(2019, 3, 22), Decimal('-275.00')],
[datetime.date(2019, 3, 22), Decimal('-350.00')],
[datetime.date(2019, 3, 22), Decimal('-175.00')]
]

data.sort(key=lambda d: d[0])
data.reverse()

for d in data:
    print(d)

Вывод:

[datetime.date(2019, 3, 29), Decimal('45000.00')]
[datetime.date(2019, 3, 29), Decimal('44819.75')]
[datetime.date(2019, 3, 28), Decimal('0.00')]
[datetime.date(2019, 3, 22), Decimal('-175.00')]
[datetime.date(2019, 3, 22), Decimal('-350.00')]
[datetime.date(2019, 3, 22), Decimal('-275.00')]
0 голосов
/ 22 марта 2020

Таким образом, вы хотите отсортировать по дате, а в случае t ie сортировать по обратной текущей позиции. Легко:

[value for _, value in sorted(enumerate(dates), key=lambda x: (x[1], -x[0]), reverse=True)]

Результаты:

>>> pprint([val for _, val in sorted(enumerate(seq), key=lambda x: (x[1][0], -x[0]), reverse=True)])
[[datetime.date(2019, 3, 29), Decimal('44819.75')],
 [datetime.date(2019, 3, 29), Decimal('45000.00')],
 [datetime.date(2019, 3, 28), Decimal('0.00')],
 [datetime.date(2019, 3, 22), Decimal('-275.00')],
 [datetime.date(2019, 3, 22), Decimal('-350.00')],
 [datetime.date(2019, 3, 22), Decimal('-175.00')]]
0 голосов
/ 22 марта 2020

Вот способ, который использует промежуточную структуру данных:

>>> from collections import defaultdict

>>> dd = defaultdict(list)
>>> for x,y in data:
    dd[x].insert(0,y) #key is date, value is reverse list of Decimals for each date
>>> dd
defaultdict(<type 'list'>, {datetime.date(2019, 3, 29): [Decimal('45000.00'), Decimal('44819.75')], datetime.date(2019, 3, 28): [Decimal('0.00')], datetime.date(2019, 3, 22): [Decimal('-175.00'), Decimal('-350.00'), Decimal('-275.00')]})
>>> dataout = [[x,y] for x in sorted(dd.keys(),reverse=True) for y in dd[x]]
>>> dataout
[
 [datetime.date(2019, 3, 29), Decimal('45000.00')],
 [datetime.date(2019, 3, 29), Decimal('44819.75')],
 [datetime.date(2019, 3, 28), Decimal('0.00')],
 [datetime.date(2019, 3, 22), Decimal('-175.00')],
 [datetime.date(2019, 3, 22), Decimal('-350.00')],
 [datetime.date(2019, 3, 22), Decimal('-275.00')]
]
0 голосов
/ 22 марта 2020

В Python, когда вы сортируете список итераций (например, list, tuple и т. Д. c), списки элементов сортируются в соответствии со своими значениями в первом индексе. Если они совпадают, то сравниваются значения по следующему индексу.
Это связано с обращением десятичных дробей.

Ниже вы хотите:

... import datetime
... from decimal import Decimal
... from operator import itemgetter
... from itertools import groupby, chain
... 
... data = [
...     [datetime.date(2019, 3, 29), Decimal('44819.75')],
...     [datetime.date(2019, 3, 29), Decimal('45000.00')],
...     [datetime.date(2019, 3, 28), Decimal('0.00')],
...     [datetime.date(2019, 3, 22), Decimal('-275.00')],
...     [datetime.date(2019, 3, 22), Decimal('-350.00')],
...     [datetime.date(2019, 3, 22), Decimal('-175.00')]
... ]
... date_sorted_data = sorted(data, key=itemgetter(0), reverse=True)
... 
... result = list(
...     chain.from_iterable(
...         [
...             reversed(list(g)) 
...             for k, g in groupby(
...                 date_sorted_data, key=itemgetter(0)
...             )
...         ]
...     )
... )
... 
... print(result)
... 
[[datetime.date(2019, 3, 29), Decimal('45000.00')], [datetime.date(2019, 3, 29), Decimal('44819.75')], [datetime.date(2019, 3, 28), Decimal('0.00')], [datetime.date(2019, 3, 22), Decimal('-175.00')], [datetime.date(2019, 3, 22), Decimal('-350.00')], [datetime.date(2019, 3, 22), Decimal('-275.00')]]
0 голосов
/ 22 марта 2020
In [4]: dates = [
   ...: [datetime.date(2019, 3, 29), Decimal('44819.75')],
   ...: [datetime.date(2019, 3, 29), Decimal('45000.00')],
   ...: [datetime.date(2019, 3, 28), Decimal('0.00')],
   ...: [datetime.date(2019, 3, 22), Decimal('-275.00')],
   ...: [datetime.date(2019, 3, 22), Decimal('-350.00')],
   ...: [datetime.date(2019, 3, 22), Decimal('-175.00')]
   ...: ]

In [5]: sorted(dates, key=lambda x: (x[0].day, x[1]), reverse=True)

Out[5]:
[[datetime.date(2019, 3, 29), Decimal('45000.00')],
 [datetime.date(2019, 3, 29), Decimal('44819.75')],
 [datetime.date(2019, 3, 28), Decimal('0.00')],
 [datetime.date(2019, 3, 22), Decimal('-175.00')],
 [datetime.date(2019, 3, 22), Decimal('-275.00')],
 [datetime.date(2019, 3, 22), Decimal('-350.00')]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...