Изменение формы переменной Numpy

#python #list #numpy

#python #Список #numpy

Вопрос:

В Python у меня есть список значений, которые мне нужно разбить на меньшие массивы на основе значений в другом массиве. Например, у меня есть этот массив цифр:

 [6,5,3,1,4,3,2,4,1,3,6,1]
  

И мне нужно, чтобы она превратилась в этот массив:

 [[6,5,3],[1,4],[3,2],[4],[1,3,6],[1]]
  

Учитывая, что у меня есть этот массив, определяющий длины разделенных частей:

 [3,2,2,1,3,1]
  

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

Комментарии:

1.Уточните, когда вы говорите о Python lists и когда numpy arrays . Или вы просто используете array для обозначения того же, что и списки.

2. numpy действительно имеет split функцию, но все, что она делает, это занимает кучу фрагментов. Придерживаюсь вашего списка: [alist[0:3], alist[3:5], alist[5:7], alist[7:8],....] .

Ответ №1:

Если вы не хотите нести дополнительные расходы на удаление элементов из списка, вот один из способов сделать это, используя itertools.islice() :

 iterator = iter(my_list)
my_result = [list(islice(iterator, length)) for length in my_lens]
  

Тестируем это:

 from itertools import islice

my_list = [6,5,3,1,4,3,2,4,1,3,6,1]
my_lens = [3,2,2,1,3,1]

iterator = iter(my_list)
my_result = [list(islice(iterator, length)) for length in my_lens]
print(my_result)
  

Вывод:

 [[6, 5, 3], [1, 4], [3, 2], [4], [1, 3, 6], [1]]
  

Комментарии:

1. Вам не нужно 0 в качестве начала islice, это значение по умолчанию. Просто размер (итератор, длина).

2. @Veky, верно, но, возможно, упоминание начала в любом случае улучшает читаемость.

3. Для меня это то же самое, что и range. Если вы регулярно пишете range(0,5) вместо range (5), то я вас понимаю, но сомневаюсь в этом. :-]

4. @Veky, ты выиграл — я регулярно использую range(5) вместо range(0,5) . Я отредактирую ответ, чтобы избавиться от 0

5. На самом деле, я понимаю ваше беспокойство. Этот параметр «start» — единственное место в ядре Python, где аргументы по умолчанию идут не «с конца» -1, -2, -3, а -1, -3, -2. Но поскольку мы так привыкли к этому с range, было бы стыдно не использовать ту же идиому здесь; более того, поскольку, по моему опыту, мы ошибаемся с самого начала даже чаще, чем в диапазоне от 0.

Ответ №2:

Предполагая, что сумма длин всегда равна длине списка, вы можете использовать list.pop(0) :

 l = [6,5,3,1,4,3,2,4,1,3,6,1]
[[l.pop(0) for _ in range(i)] for i in [3,2,2,1,3,1]]
  

Вывод:

 [[6, 5, 3], [1, 4], [3, 2], [4], [1, 3, 6], [1]]
  

Примечание: list.pop по умолчанию всплывает последний элемент, поэтому list.pop(0) используется.

Ответ №3:

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

 from itertools import accumulate, tee, chain
from operator import itemgetter

i2, i3 = tee(accumulate([3, 2, 2, 1, 3, 2]))
slicer = itemgetter(*map(slice, chain([None], i2), i3))

>>> print(slicer([6, 5, 3, 1, 4, 3, 2, 4, 1, 3, 6, 1]))
([6, 5, 3], [1, 4], [3, 2], [4], [1, 3, 6], [1])
  

Ответ №4:

Решение с использованием генератора приведено ниже.

 def splitter(arr, lengths):
    assert(sum(lengths) == len(arr))
    i, s = 0, 0
    for l in lengths:
        s = s   l
        yield arr[i:s]
        i = i   l
  

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

 my_list = [6, 5, 3, 1, 4, 3, 2, 4, 1, 3, 6, 1]
my_lens = [3, 2, 2, 1, 3, 1]

ans = [x for x in splitter(my_list, my_lens)]
print(ans)
# [[6, 5, 3], [1, 4], [3, 2], [4], [1, 3, 6], [1]]

text = 'a quick brown fox jumped over the lazy dog'
lengths = [2, 6, 6, 4, 7, 5, 4, 5, 3]
print([x for x in splitter(text, lengths)])
# ['a ', 'quick ', 'brown ', 'fox ', 'jumped ', 'over ', 'the ', 'lazy ', 'dog']

my_tuple = (6, 5, 3, 1, 4, 3, 2, 4, 1, 3, 6, 1)
my_lens = [3, 2, 2, 1, 3, 1]
print(tuple(x for x in splitter(my_tuple, my_lens)))
# ((6, 5, 3), (1, 4), (3, 2), (4,), (1, 3, 6), (1,))