Рекурсия при перечислении: заполнение / нормализация размера неравномерного вложенного списка списков [списков ...] в массив с пустым массивом - рекурсивно - PullRequest
0 голосов
/ 22 июня 2019

Я пытаюсь сделать более надежную нормализацию заполнения / размера для получения списка списков в формате пустого массива.

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

У меня естьфункция, которая получает нужную форму np.array.

Теперь при заполнении массива рекурсия с перечислением стала проблематичной - из-за отслеживания индексов на каждой глубине.

Проще говоря:

Мне нужна рекурсивная функция, которая делает это на неопределенной глубине:

# fill in the values of the ndarray `mat` with values from l
for row_ix, row in enumerate(l):
    for col_ix, col in enumerate(row):
        for val_ix, val in enumerate(col):
                   # ...
                   # ...
            mat[row_ix, col_ix, val_ix] = l[row_ix][col_ix][val_ix]

Extra Detail


Вот мой MCVE (минимальный, полный и проверяемый пример) для желаемого результата / функциональности:

import numpy as np

# nested shapes of each list ( 2, [2, 4], [ [5,7], [3,7,4,9] ])
# desired shape (max of level) --> (2,4,9)
l = [[[1,2,5,6,7],
       [0,2,5,34,5,6,7]],
      [[5,6,7],
       [0,2,5,7,34,5,7],
       [0,5,6,7],
       [1,2,3,4,5,6,7,8,9]]]

def nested_list_shape(lst):
#     Provides the *maximum* length of each list at each depth of the nested list.
#     (Designed to take uneven nested lists)
    if not isinstance(lst[0], list):
        return tuple([len(lst)])
    return tuple([len(lst)]) + max([nested_list_shape(i) for i in lst])

shape = nested_list_shape(l) # (2,4,9)
mat = np.zeros(shape) # make numpy array of shape (2,4,9)

# fill in the values of the ndarray `mat` with values from l
for row_ix, row in enumerate(l):
    for col_ix, col in enumerate(row):
        for val_ix, val in enumerate(col):
            mat[row_ix, col_ix, val_ix] = l[row_ix][col_ix][val_ix]
print(mat)

И вот что у меня естьпопытка до сих пор:

import numpy as np

# nested shapes of each list ( 2, [2, 4], [ [5,7], [3,7,4,9] ])
# desired shape (max of level) --> (2,4,9)
l = [[[1,2,5,6,7],
       [0,2,5,34,5,6,7]],
      [[5,6,7],
       [0,2,5,7,34,5,7],
       [0,5,6,7],
       [1,2,3,4,5,6,7,8,9]]]

def nested_list_shape(lst):
#     Provides the *maximum* length of each list at each depth of the nested list.
#     (Designed to take uneven nested lists)
    if not isinstance(lst[0], list):
        return tuple([len(lst)])
    return tuple([len(lst)]) + max([nested_list_shape(i) for i in lst])

# Useful for setting values in nested list
def get_element(lst, idxs):
#     l[x][y][z][t] <==> get_element(l, [x,y,z,t])
#
#     Given a list (e.g. `l = [[2,3],[5,6,7]]`),
#         index the elements with an list of indices (one value for each depth)
#         (e.g. if `idxs = [1,1]` then get_element returns the equivalent of l[1][1])
    if len(idxs) == 1:
        return lst[idxs[0]]
    else:
        return get_element(lst[idxs[0]], idxs[1:])

# ::Problem function::
def fill_mat(lst):
    # Create numpy array for list to fill
    shape = nested_list_shape(lst)
    depth = len(shape)
    x = np.zeros(shape)

    # Use list of indices to keep track of location within the nested enumerations
    ixs = [0] * depth

    # ::PROBLEM::
    # Recursive setting of ndarray values with nested list values
    # d = depth of recursion
    # l = list at that depth of recursion
    # lst = full nested list
    def r(l, ixs, d = 0):
        for ix, item in enumerate(l):
            # Change list of indices to match the position
            ixs[d] = ix
            # if the item is a value, we reach the max depth
            # so here we set the values
            if not isinstance(item, list):
                x[tuple(ixs)] = get_element(lst, ixs)
            else:
                # increase the depth if we see a nested list (but then how to decrease ... :/)
                d += 1
                # return statement should likely go here, and somehow return x
                r(item, ixs, d)
        return x # ?? bad use of recursion
    return r(lst, ixs)

shape = nested_list_shape(l) # (2,4,9)
mat = np.zeros(shape) # make numpy array of shape (2,4,9)

# fill in the values of the ndarray `mat` with values from l
print(fill_mat(l))

Функция get_element делает l[row_ix][col_ix][val_ix] эквивалентной функцией с ixs = [row_ix, col_ix, val_ix], но все еще существует проблема с отслеживанием каждого из них.

Кто-нибудь знаком с более простым методом рекурсивной обработки этих индексов?

1 Ответ

0 голосов
/ 23 июня 2019

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

import numpy as np
l = [[[1,2,3,4,5,6,7,8,9,10],
       [0,2,5],
       [6,7,8,9,10],
       [0,2,5],
       [0,2,5,6,7],
       [0,2,5,34,5,6,7]],
      [[2,5],
       [6,7],
       [7,8,9]],
      [[2,5],
       [6,7],
       [7,8,9]],
      [[2,5],
       [6,7],
       [7,8,9]]]

def nested_list_shape(lst):
#     Provides the *maximum* length of each list at each depth of the nested list.
#     (Designed to take uneven nested lists)
    if not isinstance(lst[0], list):
        return tuple([len(lst)])
    return tuple([len(lst)]) + max([nested_list_shape(i) for i in lst])

def iterate_nested_array(array, index=()):
    try:
        for idx, row in enumerate(array):
            yield from iterate_nested_array(row, (*index, idx))
    except TypeError: # final level
        for idx, item in enumerate(array):
            yield (*index, idx), item

def pad(array, fill_value):
    dimensions = nested_list_shape(array) #get_max_shape(array) # used my shape function, as it operated in 1/3 the time
    result = np.full(dimensions, fill_value)
    for index, value in iterate_nested_array(array):
        result[index] = value
    return result

print(pad(l, fill_value = 0))

.

output:
[[[ 1  2  3  4  5  6  7  8  9 10]

  [ 0  2  5  0  0  0  0  0  0  0]
  [ 6  7  8  9 10  0  0  0  0  0]
  [ 0  2  5  0  0  0  0  0  0  0]
  [ 0  2  5  6  7  0  0  0  0  0]
  [ 0  2  5 34  5  6  7  0  0  0]]

 [[ 2  5  0  0  0  0  0  0  0  0]
  [ 6  7  0  0  0  0  0  0  0  0]
  [ 7  8  9  0  0  0  0  0  0  0]
  [ 0  0  0  0  0  0  0  0  0  0]
  [ 0  0  0  0  0  0  0  0  0  0]
  [ 0  0  0  0  0  0  0  0  0  0]]

 [[ 2  5  0  0  0  0  0  0  0  0]
  [ 6  7  0  0  0  0  0  0  0  0]
  [ 7  8  9  0  0  0  0  0  0  0]
  [ 0  0  0  0  0  0  0  0  0  0]
  [ 0  0  0  0  0  0  0  0  0  0]
  [ 0  0  0  0  0  0  0  0  0  0]]

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