Как изменить список Python без NumPy - PullRequest
4 голосов
/ 14 мая 2019

Как мне преобразовать список в n-мерный список

Ввод: список = [1, 2, 3, 4, 5, 6, 7, 8]
shape = [2, 2, 2]

output = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

Ответы [ 4 ]

3 голосов
/ 14 мая 2019

Этот рекурсивный подход должен сработать.

lst = [1, 2, 3, 4, 5, 6, 7, 8]
shape = [2, 2, 2]

from functools import reduce 
from operator import mul

def reshape(lst, shape):
    if len(shape) == 1:
        return lst
    n = reduce(mul, shape[1:])
    return [reshape(lst[i*n:(i+1)*n], shape[1:]) for i in range(len(lst)//n)]

reshape(lst, shape)

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

assert reduce(mul, shape) == len(lst)
1 голос
/ 14 мая 2019

Вот подход, использующий группер один раз для каждого измерения, кроме первого:

import functools as ft

# example
L = list(range(2*3*4))
S = 2,3,4

# if tuples are acceptable 
tuple(ft.reduce(lambda x, y: zip(*y*(x,)), (iter(L), *S[:0:-1])))
# (((0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)), ((12, 13, 14, 15), (16, 17, 18, 19), (20, 21, 22, 23)))

# if it must be lists
list(ft.reduce(lambda x, y: map(list, zip(*y*(x,))), (iter(L), *S[:0:-1])))
# [[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]], [[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]]
1 голос
/ 14 мая 2019

Код ниже должен помочь.

Решение, приведенное ниже, очень общее.Входной список может быть вложенным списком списков любой старой / нежелательной формы;это не обязательно должен быть список целых чисел.

Кроме того, существуют отдельные инструменты многократного использования.Например, функция all_for_one очень удобна.

РЕДАКТИРОВАТЬ:
Я не заметил что-то важное.Если вы поместите 1 s внутри параметра shape, то вы можете получить лишние вложения списка (только один список внутри списка вместо пяти или шести списков внутри списка)

Например,если форма равна [1, 1, 2]
, то возвращаемое значение может быть [[[0.1, 0.2]]] вместо [0.1, 0.2]

, длина shape - это число действительных подписок в списке вывода.
ДляНапример,

shape = [1, 2] # length 2
lyst = [[0.1, 0.2]]
print(lyst[0][0])  # valid.... no KeyError raised

Если вы хотите истинный столбец или вектор строки, тогда len(shape) должен быть 1.
Например, shape = [49] даст вам вектор строки / столбца длины49.

shape = [2] # length 2
output = [0.1, 0.2]
print(lyst[0])  

Вот код:

from operator import mul
import itertools as itts
import copy
import functools

one_for_all =  lambda one: itts.repeat(one, 1)

def all_for_one(lyst):
   """
    EXAMPLE:
        INPUT:
            [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
        OUTPUT:
            iterator to [1, 2, 3, 4, 5, 6, 7, 8]

    IN GENERAL:
        Gets iterator to all nested elements
        of a list of lists of ... of lists of lists.
    """
    # make an iterator which **IMMEDIATELY**
    # raises a `StopIteration` exception
    its = itts.repeat("", 0)

    for sublyst in lyst:

        if hasattr(sublyst, "__iter__") and id(sublyst) != id(lyst):
            # Be careful ....
            #
            # "string"[0] == "s"[0] == "s"[0][0][0][0][0][0]...
            #
            # do not drill down while `sublyst` has an "__iter__" method
            # do not drill down while `sublyst` has a `__getitem__` method
            #
            it = all_for_one(sublyst)
        else:
            it = one_for_all(sublyst)

        # concatenate results to what we had previously
        its = itts.chain(its, it)

    return its



merged = list(all_for_one([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]))
print("merged == ", merged)




def reshape(xread_lyst, xshape):
    """
    similar to `numpy.reshape`

    EXAMPLE:         

        lyst  = [1, 2, 3, 4, 5, 6, 7, 8]
        shape = [2, 2, 2]        
        result = reshape(lyst)
        print(result)

         result ==
         [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]


    For this function, input parameter `xshape` can be
    any iterable containing at least one element.
    `xshape` is not required to be a tuple, but it can be.

    The length of xshape should be equal to the number
    of desired list nestings

    If you want a list of integers: len(xshape) == 1
    If you want a list of lists:    len(xshape) == 2
    If you want a list of lists of lists: len(xshape) == 3

    If xshape = [1, 2],
    outermost list has 1 element
    that one element is a list of 2 elements.
    result == [[1, 2]]

    If xshape == [2]
    outermost list has 2 elements
    those 2 elements are non-lists:
    result: [1, 2] 

    If xshape = [2, 2],
    outermost list has 2 elements
    each element is a list of 2 elements.
    result == [[1, 2] [3, 4]]


    """
    # BEGIN SANITIZING INPUTS

    # unfortunately, iterators are not re-usable
    # Also, they don't have `len` methods

    iread_lyst = [x for x in ReshapeTools.unnest(xread_lyst)]
    ishape = [x for x in self.unnest(xshape)]

    number_of_elements = functools.reduce(mul, ishape, 1)

    if(number_of_elements != len(iread_lyst)):
        msg = [str(x) for x in [
            "\nAn array having dimensions ", ishape,
            "\nMust contain ", number_of_elements, " element(s).",
            "\nHowever, we were only given ", len(iread_lyst), " element(s)."
        ]]
        if len(iread_lyst) < 10:
             msg.append('\nList before reshape: ')
             msg.append(str([str(x)[:5] for x in iread_lyst]))
        raise TypeError(''.join(msg))
    ishape = iter(ishape)
    iread_lyst = iter(iread_lyst)
    # END SANITATIZATION OF INPUTS
    write_parent = list()
    parent_list_len = next(ishape)
    try:
        child_list_len = next(ishape)
        for _ in range(0, parent_list_len):
            write_child = []
            write_parent.append(write_child)
            i_reshape(write_child, iread_lyst, child_list_len, copy.copy(ishape))
    except StopIteration:
        for _ in range(0, parent_list_len):
            write_child = next(iread_lyst)
            write_parent.append(write_child)
    return write_parent


def ilyst_reshape(write_parent, iread_lyst, parent_list_len, ishape):
    """
    You really shouldn't call this function directly.
    Try calling `reshape` instead

    The `i` in the name of this function stands for "internal"
    """
    try:
        child_list_len = next(ishape)
        for _ in range(0, parent_list_len):
            write_child = []
            write_parent.append(write_child)
            ilyst_reshape(write_child, iread_lyst, child_list_len, copy.copy(ishape))
    except StopIteration:
        for _ in range(0, parent_list_len):
            write_child = next(iread_lyst)
            write_parent.append(write_child)
    return None

three_dee_mat = reshape(merged, [2, 2, 2])
print("three_dee_mat == ", three_dee_mat)
1 голос
/ 14 мая 2019

Не особенно элегантно:

from functools import reduce
from itertools import islice
l=[1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4]
s=[2,3,4]
if s and reduce(lambda x,y:x*y, s) == len(l):
    # if number of elements matches product of dimensions, 
    # the first dimension is actually redundant
    s=[1:]
else:
    print("length of input list does not match shape")
    return
while s:
  size = s.pop()  # how many elements for this dimension
  #split the list based on the size of the dimension
  it=iter(l)
  l = list(iter(lambda:list(islice(it,size)),[]))

# [[[1, 2, 3, 4], [5, 6, 7, 8], [9, 0, 1, 2]], 
#  [[3, 4, 5, 6], [7, 8, 9, 0], [1, 2, 3, 4]]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...