Python - Быстрый способ выборки данных из массива при изменении размера выборки - PullRequest
2 голосов
/ 26 февраля 2020

Я пытаюсь выбрать данные из списка целых чисел. Сложность в том, что каждый образец должен иметь разный размер для эмуляции некоторых других данных, которые у меня есть. Я сейчас делаю для l oop, который может выполнить эту работу, но мне было просто интересно, есть ли более быстрые способы, о которых я не знаю.

Поскольку я думаю, что random.sample должен быть быстрым, я делаю:

result = []
for i in range(100000):
    size = list_of_sizes[i]
    result.append(random.sample(data, size))

Таким образом, я получаю что-то вроде:

>>>list_of_sizes
    [3, 4, 1, 2,...]

>>>result
    [[1, 2, 3],
     [3, 6, 2, 8],
     [9],
     [10, 100],
     ...]

I пытались использовать np.random.choice(data, size, replace=False) и random.sample(data, k=size), но они не позволяют задавать массив разных размеров для векторизации операции (когда np.random.choice принимает массив в параметре size, он создает тензор, форма выходного сигнала которого равна это size, но не массив образцов). В идеале я бы ожидал что-то вроде:

>>>np.random.choice(data, list_of_sizes, replace=False)
    [[1, 2, 3],
     [3, 6, 2, 8],
     [9],
     [10, 100],
     ...]

Ответы [ 2 ]

3 голосов
/ 26 февраля 2020

Кажется, что np.random.choice действительно не оптимизирован для выбора с заменой. Тем не менее, вы можете получить более высокую производительность, используя Generator.choice, как обсуждено здесь .

Я вижу ускорение в 14 раз для ваших параметров:

data = np.arange(10**6)
sample_sizes = np.random.randint(1, 70_000, 100)

def f(data, sample_sizes):
  result = []
  for s in sample_sizes:
    result.append(np.random.choice(data, s, replace=False))

def f2(data, sample_sizes):
  g = np.random.Generator(np.random.PCG64())
  n = data.shape[0]
  return [data[g.choice(n, k, replace=False)] for k in sample_sizes]

%timeit f(data, sample_sizes)
%timeit f2(data, sample_sizes)
1 loop, best of 3: 5.18 s per loop
1 loop, best of 3: 375 ms per loop
1 голос
/ 26 февраля 2020

В зависимости от вашего оборудования, а также от размеров данных использование многопроцессорной обработки может значительно ускорить работу. Это должно быть оценено для вашей конкретной установки проблемы c, однако. Например, используя multiprocessing.pool.Pool:

from functools import partial
from multiprocessing.pool import Pool

with Pool() as pool:
    result = pool.map(partial(sample, data), sizes)

Сравнение производительности

Вот несколько примеров результатов (с использованием 4 ядер ЦП):

from functools import partial
from multiprocessing.pool import Pool
from random import choices, sample
from statistics import mean, stdev
import time


def baseline(data, sizes):
    return [sample(data, k) for k in sizes]


def multiprocessing(data, sizes):
    with Pool(4) as pool:
        return pool.map(partial(sample, data), sizes)


def timeit(f, *args, n=7):
    timings = []
    for __ in range(n):
        t_start = time.time()  # using time because of multiprocessing
        f(*args)
        t_stop = time.time()
        timings.append(t_stop - t_start)
    print(f'[{f.__name__}] {mean(timings):.2f} +/- {stdev(timings):.2f} s')


data = list(range(1_000_000))
sizes = choices(range(max(data) // 100), k=1_000)

timeit(baseline, data, sizes)
timeit(multiprocessing, data, sizes)

который дает:

[baseline] 3.19 +/- 0.07 s
[multiprocessing] 2.10 +/- 0.02 s

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

...