Предположим, у меня есть два массива одинаковой длины:
a = [0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,1]
b = [0,1,1,0,1,0,0,1,1,0,0,1,1,0,1,0,0]
Теперь я хочу выбрать элементы из этих двух массивов в последовательности, заданной так, чтобы они формировали новый массив той же длины, что и a & b, путем случайного выбора значений между a & b в соотношении a: b = 4.68, то есть для Каждое 1 значение, выбранное из a, должно быть 4.68 значений, выбранных из b в результирующем массиве.
Таким образом, результирующий массив может выглядеть примерно так:
res = [0,1,1,0,1, 1(from a) ,0(from a),1,1,0,0,1,1,0, 0(from a),0,0]
массив res имеет: первые 5 значений от b, 6 и 7 от a, 8-14 от b, 15 от a, 16-17 от b
Общее соотношение значений от a: b в данном примере массива res составляет: b 4,67 (от a = 3, от b = 14)
Таким образом, между двумя массивами значения должны выбираться случайным образом, однако последовательность должна поддерживаться, т.е. Не может принимать 7-е значение из одного массива и 3-е значение из другого. Если значение, которое нужно заполнить в результирующем массиве, равно 3, то выбор между 3-й элемент обоих входных массивов в произвольном порядке. Также необходимо поддерживать общее соотношение.
Не могли бы вы помочь мне в разработке эффективного Pythonic способа достижения этого результирующего решения? Решение не обязательно должно быть согласовано с каждым прогоном по значениям
Заимствуя расчет a_count
из ответа Barmar (потому что он, кажется, работает, и я не могу потрудиться заново его изобрести), это решение сохраняет порядок значений, выбранных из a
и b
:
from future_builtins import zip # Only on Python 2, to avoid temporary list of tuples
import random
# int() unnecessary on Python 3
a_count = int(round(1/(1 + 4.68) * len(a)))
# Use range on Python 3, xrange on Python 2, to avoid making actual list
a_indices = frozenset(random.sample(xrange(len(a)), a_count))
res = [aval if i in a_indices else bval for i, (aval, bval) in enumerate(zip(a, b))]
Основная идея здесь заключается в том, что вы определяете, сколько a
значения вам нужно, получить уникальный образец из возможных индексов такого размера, а затем итерация и a
b
параллельно, сохраняя a
значение для выбранных показателей, а b
значения для всех другие.
Если вам не нравится сложность понимания list
, вы можете использовать другой подход, копируя b
, затем заполняя значения a
одно за другим:
res = b[:] # Copy b in its entirety
# Replace selected indices with a values
# No need to convert to frozenset for efficiency here, and it clean
# enough to just iterate the sample directly without storing it
for i in random.sample(xrange(len(a)), a_count):
res[i] = a[i]
int
(уже отредактировано в), потому что round
в Python 2 всегда возвращает float
(в Python 3 он возвращает float
только при передаче второго аргумента, а int
для один аргумент). Примечание: Python 2 достигает конца срока службы менее чем за год . Новый код должен действительно предназначаться для Python 3.
Я считаю, что это должно работать. Вы указываете, сколько вы хотите от (вы можете просто использовать свое соотношение, чтобы выяснить это число), вы случайным образом генерируете "маску" чисел и выбираете из или, основываясь на срезе (обратите внимание, что вы только сортируете, чтобы выяснить, отсечка, но вы используете несортированную маску позже)
import numpy as np
a = [0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,1]
b = [0,1,1,0,1,0,0,1,1,0,0,1,1,0,1,0,0]
mask = np.random.random(len(a))
from_a = 3
cutoff = np.sort(mask)[from_a]
res = []
for i in range(len(a)):
if (mask[i]>=cutoff):
res.append(a[i])
else:
res.append(b[i])