Нахождение разницы между последовательными числами в списке (Python)

Учитывая список чисел, я пытаюсь написать код, который находит разницу между последовательными элементами. Например, A = [1, 10, 100, 50, 40], поэтому вывод функции должен быть [0, 9, 90, 50, 10]. Вот что я до сих пор пытался использовать рекурсию:

def deviation(A):
    if len(A) < 2:
        return
    else:
        return [abs(A[0]-A[1])] + [deviation(A[1: ])]

Однако результат, который я получаю (используя приведенный выше пример A в качестве ввода), равен [9, [90, [50, [10, None]]]]. Как правильно отформатировать скобки? (Я пытался угадать и проверить, но это самое близкое, что я получил) И как мне написать это, когда он вычитает текущий элемент из предыдущего элемента, не получая ошибки индекса для первого элемента? Я все еще хочу, чтобы первый элемент выходного списка был равен нулю, но я не знаю, как это сделать, используя рекурсию, и по какой-то причине это кажется мне лучшим путем.


person user3758443    schedule 07.07.2014    source источник


Ответы (6)


Ты можешь сделать:

[y-x for x, y in zip(A[:-1], A[1:])] 


>>> A = [1, 10, 100, 50, 40]
>>> [y-x for x, y in zip(A[:-1], A[1:])]
[9, 90, -50, -10]

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

Объяснение:

Лучшее объяснение, которое вы можете получить, это просто распечатать каждую часть понимания списка.

  • A[:-1] возвращает список без последнего элемента: [1, 10, 100, 50]
  • A[1:] возвращает список без первого элемента: [10, 100, 50, 40]
  • zip(A[:-1], A[1:]) возвращает [(1, 10), (10, 100), (100, 50), (50, 40)]
  • Последний шаг — просто вернуть разницу в каждом кортеже.
person Maroun    schedule 07.07.2014
comment
Вы забыли abs, но +1 все равно. - person Fred Foo; 07.07.2014
comment
Однако очень неэффективно, поскольку он создает 3 промежуточных списка того же размера (+/- 1), что и исходный список. - person freakish; 07.07.2014
comment
По какой-то причине первым элементом должен быть 0. - person Sukrit Kalra; 07.07.2014
comment
@SukritKalra Можно легко добавить, но это действительно странно. - person Maroun; 07.07.2014

Самое простое (самое ленивое) решение - использовать функцию diff numpy:

>>> A = [1, 10, 100, 50, 40]
>>> np.diff(A)
array([  9,  90, -50, -10])

Если вам нужно абсолютное значение различий (как вы подразумеваете в своем вопросе), возьмите абсолютное значение массива.

person FuzzyDuck    schedule 14.07.2014

Вы можете сделать понимание списка:

>>> A = [1, 10, 100, 50, 40]
>>> l=[A[0]]+A
>>> [abs(l[i-1]-l[i]) for i in range(1,len(l))]
[0, 9, 90, 50, 10]
person dawg    schedule 07.07.2014

Для более длинного рекурсивного решения, более соответствующего вашему исходному подходу:

def deviation(A) :
    if len(A) < 2 :
        return []
    else :
        return [abs(A[0]-A[1])] + deviation(A[1:])

Ваша проблема со скобками связана с вашим рекурсивным вызовом. Поскольку у вас есть [deviation(a[1: ])] в собственных скобках [], с каждым рекурсивным вызовом вы будете создавать новый список, в результате чего у вас будет множество списков в списках.

Чтобы решить проблему None, просто измените базовый вариант на пустой список []. Теперь ваша функция добавит «ничего» в конец вашего рекурсивно созданного списка, в отличие от присущего None, который идет с пустым return'

person B. S. Morganstein    schedule 07.07.2014

На самом деле рекурсия является излишним:

def deviation(A):
    yield 0
    for i in range(len(A) - 1):
        yield abs(A[i+1] - A[i])

Пример:

>>> A = [3, 5, 2]
>>> list(deviation(A))
[0, 2, 3]

EDIT: Еще одно, еще более простое и эффективное решение:

def deviation(A):
    prev = A[0]
    for el in A:
        yield abs(el - prev)
        prev = el
person freakish    schedule 07.07.2014
comment
Итерация диапазона для индексации итерируемого редко уместна. При таком подходе лучше использовать tee. - person wim; 07.07.2014
comment
@wim Код находится на Python3+. Используйте xrange в Python‹3. Кроме того, в использовании range нет ничего неуместного: это просто и эффективно. На самом деле tee является неуместным, неэффективным, сложным и излишним. - person freakish; 07.07.2014
comment
Неэффективность заключается не только в переборе диапазона, но и в вызове __getitem__ дважды для каждого элемента. Список сам по себе является итерируемым, поэтому нет необходимости повторять диапазон и вообще использовать list.__getitem__, лучше в первую очередь использовать list.__iter__. Это касается python2 и python3. - person wim; 07.07.2014
comment
@wim Во-первых, __getitem__ реализован на уровне C, и это O (1), поскольку мы имеем дело с динамическими массивами. Это очень эффективно. Итерация по списку имеет точно такую ​​же сложность. Во-вторых, посмотрите на источник tee: он делает более сложные вещи, добавляет, выталкивает и т. д. и т. д. В целом это менее эффективно. - person freakish; 07.07.2014
comment
Не могли бы вы подкрепить свои утверждения доказательствами, пожалуйста, тогда я удалю свой отрицательный голос. Мне не удалось сделать ваше решение более эффективным, чем подход с использованием tee, даже после того, как я удалил дополнительные накладные расходы на преобразование генератора в список. Мои номера здесь: ideone.com/luWFKN - person wim; 07.07.2014
comment
@wim Я не могу, я исправляюсь. В моих тестах эффективность практически одинакова (с вашей функцией wim немного быстрее). Я бы, вероятно, использовал tee, если бы эффективность имела решающее значение, в противном случае я бы придерживался range, так как решение легче понять. В любом случае, я не уверен, почему вы понизили это? Разве это не ответ на вопрос? Не стесняйтесь опубликовать свой ответ. - person freakish; 08.07.2014
comment
@wim Я также добавил еще более простое решение. - person freakish; 08.07.2014
comment
да, ваше новое решение лучше. это более эффективно, потому что теперь он повторяет итерацию напрямую, а не итерирует диапазон для индексации итерации. - person wim; 08.07.2014

person    schedule
comment
@dawg, сначала я использовал itertools попарно, но я изменил его перед голосованием за удаление, поэтому я до сих пор не могу понять, откуда взялось голосование за удаление. Но на большом списке попарно работает наравне, так что в любом случае смысла нет. - person Padraic Cunningham; 07.07.2014
comment
еще один минус, здесь очень грустные люди - person Padraic Cunningham; 07.07.2014