Как получить значения в списке Python, когда их индексы смешаны в начале и конце

Я пытался выполнить простое скользящее среднее списка с той особенностью, что конец списка является его началом, но когда, например, со списком от 1 до 9 с использованием 3 выборок, я пытаюсь:

original_list = [*range(1,10)]

print(sum(original_list[-1:2])/3) 

Выходное значение равно 0,0 и должно быть (9+1+2)/3 = 4.

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

print(original_list[-1:], original_list[:2])

# Print:
# [9] [1, 2]

Все было так, как и ожидалось, так что же не так в моем предыдущем отпечатке? Как я должен это делать?

Я попробовал сделать как отдельные, так и объединить их так:

print(sum(original_list[-1:] + original_list[:2]) / 3)

# Print:
# 4

Это правильное значение, однако, если я увеличу число образцов, например, до 5, и попробую:

import math
original_list = [*range(1,10)]

def MA(index):
    to_be_averaged = original_list[-2 + index:] + original_list[:3 + index]
    print(to_be_averaged)
    print(sum(to_be_averaged) / len(to_be_averaged))

MA(0)
# Print
# [8, 9, 1, 2, 3]
# 4.6

MA(1)
# Print
# [9, 1, 2, 3, 4]
# 3.8

MA(2)
# Print
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5]
# 4.285714285714286

Что за бардак на индексе 2, что происходит? Как я должен это делать? Я чувствую, что это должен быть простой способ получить диапазон от a до b независимо, являются ли индексы положительными или отрицательными.

original_list[-1:2] пусто. Срезы не идут назад, если вы явно не используете отрицательный шаг.
John Gordon 24.05.2024 16:12

iirc где-то в cycle есть функция collections

folen gateis 24.05.2024 16:14

Я видел это! тогда как бы вы это сделали, если бы ввели индекс исходного списка и размер выборки?

Lluis 24.05.2024 16:14

«Я чувствую, что это должен быть простой способ получить диапазон от a до b независимо от того, являются ли индексы положительными или отрицательными». Имейте в виду, что списки линейны. Поэтому иногда (если второй индекс меньше первого) вам придется объединить два подсписка вместо использования только одного.

NoDataDumpNoContribution 24.05.2024 16:35

Вас волнует скорость? Если да, то насколько велики ваши списки и образцы?

no comment 24.05.2024 17:05
Почему в 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
5
73
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Это... не так работает нарезка. Здесь есть несколько заблуждений, поэтому нам следует разобраться в них одно за другим.

Прежде всего, когда вы начинаете с отрицательного индекса на срезе, мы работаем с конца списка. Таким образом, l[-1:] -> [9] будет давать только одно значение, если вы также не используете отрицательный шаг. Если вы используете отрицательный шаг, вы получите больше значений, но он не будет повторяться так, как вы хотите, а будет работать в обратном направлении: l[-1:2:-1] -> [9, 8, 7, 6, 5, 4].

Итак, это наша первая проблема. Далее вы должны помнить, что срезы списка представляют собой полуинтервалы ([)) — они включают начальный индекс, но не конечный индекс. Таким образом, даже если бы это произошло, l[-1:2] не включало бы 2, а остановилось бы на 1.

С учетом всего вышесказанного, возможно, вам лучше всего подойдет двусторонняя очередь для поворота списка в нужное вам состояние, чтобы вы могли легко разрезать его, а затем восстановить. Бывший:

from collections import deque

d = deque(range(1,10))

def rolling(d, start, end):
  if start < 0:
    d.rotate(-start)
  print(sum(list(d)[0:end+1])/len(list(d)[0:end+1]))
  if start < 0:
    d.rotate(start)

rolling(d, -1, 2) # 4

print(d) # deque([1, 2, 3, 4, 5, 6, 7, 8, 9])

По сути, срез — это блок списка, он может пропускать значения, но не обтекает края. С помощью list[-1:2] вы говорите вернуть все значения, которые находятся после 9-го индекса и до 2-го, что, очевидно, не является ни одним из них.

Полный список такой же, как list[:n1] + list[n1:-n2] + list[-n2:], и вам нужны первый и последний фрагменты, так что вы правы в том, что вам нужно делать list[:n] и list[-n:] отдельно.

Что касается того, почему ваша функция MA вводит странное поведение для index=1, то это просто потому, что вы используете original_list[-2 + index:] вместо original_list[-2 - index:]. Когда вы ввели вход как 2, срез возвращал весь список, поэтому вы получили гораздо больше значений, чем ожидалось.

С этим исправлено:

def MA(index):
    to_be_averaged = original_list[-2 - index:] + original_list[:3 + index]
    print(to_be_averaged)
    print(sum(to_be_averaged) / len(to_be_averaged))

>>> MA(0)
[8, 9, 1, 2, 3]
4.6

>>> MA(1)
[7, 8, 9, 1, 2, 3, 4]
4.857142857142857

>>> MA(2)
[6, 7, 8, 9, 1, 2, 3, 4, 5]
5.0

itertools.cycle не работает так, как я думал, поэтому я придумал следующее:

def cycle(arr, start, size):
    print(f'start: {start}, size: {size}')
    start = start % len(arr)
    end = start+size
    cycles = end // len(arr)
    arr=arr*(1+cycles)
    res = arr[start:end]
    print(res)
    print('average:', sum(res)/len(res))
    print()

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

start: -15, size: 32
[4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8]
average: 5.15625

start: -11, size: 5
[8, 9, 1, 2, 3]
average: 4.6

start: -1, size: 3
[9, 1, 2]
average: 4.0

start: 0, size: 9
[1, 2, 3, 4, 5, 6, 7, 8, 9]
average: 5.0

start: 0, size: 18
[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]
average: 5.0

start: 58, size: 3
[5, 6, 7]
average: 6.0

Как, по вашему мнению, itertools.cycle работает?

no comment 24.05.2024 18:06
Ответ принят как подходящий

Спасибо за ответы и комментарии, это очень помогло мне разобраться в вопросе!

Однако ни один из них не обратился к главному вопросу о том, как делать скользящую среднюю в том виде, в каком она была представлена. В итоге я применил небольшие хитрости и изменил способ создания нового списка для усреднения (вместо нарезки и понимания списка).

import math
import numpy as np

def MA(index, sample):
    N = len(original_list)
    neighbors = math.floor(sample/2)
    desired_range = (np.array(range(sample)) - neighbors + index) % N
    
    to_be_averaged = [original_list[i] for i in desired_range]
    print(to_be_averaged)
    return sum(to_be_averaged)/sample

Я пытаюсь объяснить построчно:

N = len(original_list)

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

neighbors = math.floor(sample/2)

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

desired_range = (np.array(range(sample)) - neighbors + index) % N

И есть самое забавное: здесь я пытаюсь получить нужный диапазон, но в индексах, которые будут использоваться позже. Я создаю np.array, чтобы я мог выполнять операции сложения и вычитания в списке, который я создаю с помощью range, размер диапазона - это выборка, которую я хочу, и я настраиваю его так, чтобы он был предыдущим, а после образцов просто разрезаю его, вычитая соседей. , затем я добавляю нужный индекс, чтобы у меня были индексы интересующих позиций, однако может случиться так, что мы добавим слишком много и выйдем за пределы списка, поэтому использование мода % N обтекает размер списка и все, что выше N, начинается с начала списка.

Остальное — это, по сути, понимание списка в пределах желаемого диапазона, который мы уже создали, а также суммирование и деление на размер выборки.

Надеюсь, поможет!

original_list = [*range(5)]

# Tests

[print(MA(i,3)) for i in range((len(original_list)))]
# [4, 0, 1]
# 1.6666666666666667
# [0, 1, 2]
# 1.0
# [1, 2, 3]
# 2.0
# [2, 3, 4]
# 3.0
# [3, 4, 0]
# 2.3333333333333335
[print(MA(i,5)) for i in range((len(original_list)))]
# [3, 4, 0, 1, 2]
# 2.0
# [4, 0, 1, 2, 3]
# 2.0
# [0, 1, 2, 3, 4]
# 2.0
# [1, 2, 3, 4, 0]
# 2.0
# [2, 3, 4, 0, 1]
# 2.0

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