Перемешивание указанного c элемента во вложенном списке списков - PullRequest
3 голосов
/ 17 июня 2020

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

Например, у меня есть:

list1=[[['a', 'b', 'c', 1], ['a', 'b', 'c', 2]], [['a', 'b', 'c', 3], ['a', 'b', 'c', 4]], [['a', 'b', 'c', 5], ['a', 'b', 'c', 6]]]

Я хочу случайным образом перемешать 3-й элемент каждого под- list (float), сохраняя при этом остальные записи списка и их структуру внутри этого вложенного списка. Например, как таковой:

list1=[[['a', 'b', 'c', 1], ['a', 'b', 'c', 6]], [['a', 'b', 'c', 4], ['a', 'b', 'c', 2]], [['a', 'b', 'c', 3], ['a', 'b', 'c', 5]]]

До сих пор я придумал следующее:

import random

list1 = [[['a', 'b', 'c', 1], ['a', 'b', 'c', 2]], [['a', 'b', 'c', 3], ['a', 'b', 'c', 4]], [['a', 'b', 'c', 5], ['a', 'b', 'c', 6]]]

vals = [x[3] for line in list1 for x in line]
shuffled_vals = random.sample(vals,len(vals))

counter = 0
for i in range(len(list1)):
    for j in range(len(list1[i])):
        list1[i][j][3] = shuffled_vals[counter]
        counter += 1

Хотя это работает как намерение, мне было бы любопытно, есть ли еще элегантное / Pythoni c решение. Кроме того, я не уверен, насколько хорошо это масштабируется - список, который я собираюсь использовать, будет содержать несколько миллионов записей.

Любые советы по улучшению этого кода (до более Pythoni c или более эффективного ) очень признательны.

Ответы [ 2 ]

6 голосов
/ 17 июня 2020

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

Собирая это вместе, мы получаем что-то вроде этого:

from random import sample

vals = [x[3] for line in list1 for x in line]
vals = sample(vals, len(vals))

new_list = [[sub_l[:3] + [vals.pop()] for sub_l in l] for l in list1]

Здесь я также делаю использование .pop(), потому что мы можем выбросить vals, как только мы их использовали. Что спасает меня от использования чего-то вроде counter, которое у вас было.

Может быть, небольшим улучшением было бы использование мутации, при которой мы полностью контролируем объект. Использование random.sample() очень удобно, если вы хотите избежать мутации, но если вам все равно, вы можете просто заменить vals = sample(vals, len(vals)) на random.shuffle(vals). Что, как мне кажется, здесь немного лучше, так как оно более читабельно.

2 голосов
/ 17 июня 2020

Использование итератора может быть полезным:

import random
list1=[[['a', 'b', 'c', 1], ['a', 'b', 'c', 2]], [['a', 'b', 'c', 3], ['a', 'b', 'c', 4]], [['a', 'b', 'c', 5], ['a', 'b', 'c', 6]]]
mix = iter(random.sample([i[3] for j in list1 for i in j], len([i[3] for j in list1 for i in j])))
for i, v in enumerate(list1):
    for j, w in enumerate(v):
        list1[i][j][3] = next(mix)
print(list1)
[[['a', 'b', 'c', 5], ['a', 'b', 'c', 6]], [['a', 'b', 'c', 3], ['a', 'b', 'c', 2]], [['a', 'b', 'c', 1], ['a', 'b', 'c', 4]]]

Или как функция:

def shuffle_third(input_list):
    current_third = [i[3] for j in input_list for i in j]
    mix = iter(random.sample(current_third, len(current_third)))
    for i, v in enumerate(input_list):
        for j, w in enumerate(v):
            input_list[i][j][3] = next(mix)
    return input_list

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

def any_pos_recursive(input_list, pos=0):
    def recurse(next_layer, insert=False):
        if type(next_layer[0]) is list:
            for i in next_layer: recurse(i)
        elif not insert: shuffle_list.append(next_layer[pos])
    shuffle_list = []
    recurse(input_list)
    shuffle_iter = iter(random.sample(shuffle_list, len(shuffle_list)))
    print(shuffle_list)

any_pos_recursive(list1)
...