How to perform repeat padding in numpy?

Viewed 279

I have variable length data and want to pack it to batches with the size of max sample len in batch by repeating shorter samples.

For example from this

[[0, 1, 2, 3, 4], [0, 1, 2], [2, 2, 3]]

make this

[[0, 1, 2, 3, 4], [0, 1, 2, 0, 1], [2, 2, 3, 2, 2]]

3 Answers

not a numpy answer, but you can do that using itertools:

from itertools import cycle, islice

lst = [[0, 1, 2, 3, 4], [0, 1, 2], [2, 2, 3]]

n = max(len(item) for item in lst)
res = list(list(islice(cycle(item), n)) for item in lst)
print(res)  # [[0, 1, 2, 3, 4], [0, 1, 2, 0, 1], [2, 2, 3, 2, 2]]

where i use cycle to cycle over the sublists and islice to get the first n elements.

IIUC, you could do the following:

import numpy as np

data = [[0, 1, 2, 3, 4], [0, 1, 2], [2, 2, 3]]
max_len = max(map(len, data))
result = np.array([[row[i % len(row)] for i in range(max_len)] for row in data])

print(result)

Output

[[0 1 2 3 4]
 [0 1 2 0 1]
 [2 2 3 2 2]]

you can use np.resize:

from itertools import starmap

data = [[0, 1, 2, 3, 4], [0, 1, 2], [2, 2, 3]]
m = len(max(data, key=len))
r = np.array(list(starmap(np.resize, ((e, m) for e in data))))
print(r)

output:

[[0 1 2 3 4]
 [0 1 2 0 1]
 [2 2 3 2 2]]

here is a simple benchmark with the proposed solutions:

enter image description here

import numpy as np
from itertools import cycle, islice
from random import randint, sample
from itertools import starmap


from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()

@b.add_function()
def hiroprotagonist(lst):
    n = max(len(item) for item in lst)
    res = list(list(islice(cycle(item), n)) for item in lst)
    np.array(res)

@b.add_function()
def kederrac(data):

    m = len(max(data, key=len))
    r = np.array(list(starmap(np.resize, ((e, m) for e in data))))

@b.add_function()
def DaniMesejo(data):

    max_len = max(map(len, data))
    result = np.array([[row[i % len(row)] for i in range(max_len)] for row in data])


@b.add_arguments('Number of sublists')
def argument_provider():
    for exp in range(2, 14):
        size = 2**exp
        yield size, [sample(range(size),  randint(1, min(1000, size))) for _ in range(size)]

r = b.run()
r.plot()
Related