Chcę wiedzieć, jak mogę uzupełnić tablicę numpy 2D zerami za pomocą Pythona 2.6.6 z Numpy w wersji 1.5.0. Przepraszam! Ale to są moje ograniczenia. Dlatego nie mogę użyć np.pad
. Na przykład chcę dopełnić a
zerami tak, aby pasował do kształtu b
. Powód, dla którego chcę to zrobić, jest taki, że mogę:
b-a
takie że
>>> a
array([[ 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1.]])
>>> b
array([[ 3., 3., 3., 3., 3., 3.],
[ 3., 3., 3., 3., 3., 3.],
[ 3., 3., 3., 3., 3., 3.],
[ 3., 3., 3., 3., 3., 3.]])
>>> c
array([[1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0]])
Jedyny sposób, w jaki mogę o tym pomyśleć, to dołączanie, jednak wydaje się to dość brzydkie. czy jest możliwe użycie czystszego rozwiązania b.shape
?
Edytuj, dziękuję za odpowiedź MSeiferts. Musiałem to trochę posprzątać i oto co dostałem:
def pad(array, reference_shape, offsets):
"""
array: Array to be padded
reference_shape: tuple of size of ndarray to create
offsets: list of offsets (number of elements must be equal to the dimension of the array)
will throw a ValueError if offsets is too big and the reference_shape cannot handle the offsets
"""
# Create an array of zeros with the reference shape
result = np.zeros(reference_shape)
# Create a list of slices from offset to offset + shape in each dimension
insertHere = [slice(offsets[dim], offsets[dim] + array.shape[dim]) for dim in range(array.ndim)]
# Insert the array in the result at the specified offsets
result[insertHere] = array
return result
padded = np.zeros(b.shape)
padded[tuple(slice(0,n) for n in a.shape)] = a
NumPy 1.7.0 (kiedy
numpy.pad
został dodany) jest teraz dość stary (został wydany w 2013 roku), więc mimo że pytanie dotyczyło sposobu bez użycia tej funkcji, pomyślałem, że warto wiedzieć, jak można to osiągnąć za pomocąnumpy.pad
.To całkiem proste:
>>> import numpy as np >>> a = np.array([[ 1., 1., 1., 1., 1.], ... [ 1., 1., 1., 1., 1.], ... [ 1., 1., 1., 1., 1.]]) >>> np.pad(a, [(0, 1), (0, 1)], mode='constant') array([[ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0.]])
W tym przypadku użyłem, że
0
jest to domyślna wartośćmode='constant'
. Ale można go również określić, przekazując go jawnie:>>> np.pad(a, [(0, 1), (0, 1)], mode='constant', constant_values=0) array([[ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0.]])
Na wszelki wypadek drugi argument (
[(0, 1), (0, 1)]
) wydaje się zagmatwany: każdy element listy (w tym przypadku krotka) odpowiada wymiarowi, a element w nim reprezentuje wypełnienie przed (pierwszy element) i po (drugi element). Więc:W tym przypadku dopełnienie dla pierwszej i drugiej osi jest identyczne, więc można też po prostu podać 2-krotkę:
>>> np.pad(a, (0, 1), mode='constant') array([[ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0.]])
W przypadku, gdy wypełnienie przed i po jest identyczne, można nawet pominąć krotkę (jednak nie ma to zastosowania w tym przypadku):
>>> np.pad(a, 1, mode='constant') array([[ 0., 0., 0., 0., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0.], [ 0., 1., 1., 1., 1., 1., 0.], [ 0., 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0., 0.]])
Lub jeśli dopełnienie przed i po jest identyczne, ale różne dla osi, możesz również pominąć drugi argument w krotkach wewnętrznych:
>>> np.pad(a, [(1, ), (2, )], mode='constant') array([[ 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
Jednak wolę zawsze używać tego wyraźnego, ponieważ łatwo jest popełniać błędy (gdy oczekiwania NumPys różnią się od twoich intencji):
>>> np.pad(a, [1, 2], mode='constant') array([[ 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0.]])
Tutaj NumPy myśli, że chcesz wyłożyć wszystkie osie 1 elementem przed i 2 elementami po każdej osi! Nawet jeśli zamierzałeś wypełnić 1 element na osi 1 i 2 elementy na osi 2.
Użyłem list krotek do wypełnienia, zauważ, że to tylko „moja konwencja”, możesz też użyć list list lub krotek krotek, a nawet krotek tablic. NumPy po prostu sprawdza długość argumentu (lub jeśli nie ma on długości) i długość każdego elementu (lub jeśli ma długość)!
źródło
mode='constant'
jest rozsądną wartością domyślną, więc wypełnienie zerami można osiągnąć bez potrzeby stosowania jakichkolwiek opcjonalnych słów kluczowych, co prowadzi do nieco bardziej czytelnego kodu.Rozumiem, że głównym problemem jest to, że musisz obliczyć,
d=b-a
ale twoje tablice mają różne rozmiary. Nie ma potrzeby stosowania pośredniej wyściółkic
Możesz rozwiązać ten problem bez dopełnienia:
import numpy as np a = np.array([[ 1., 1., 1., 1., 1.], [ 1., 1., 1., 1., 1.], [ 1., 1., 1., 1., 1.]]) b = np.array([[ 3., 3., 3., 3., 3., 3.], [ 3., 3., 3., 3., 3., 3.], [ 3., 3., 3., 3., 3., 3.], [ 3., 3., 3., 3., 3., 3.]]) d = b.copy() d[:a.shape[0],:a.shape[1]] -= a print d
Wynik:
[[ 2. 2. 2. 2. 2. 3.] [ 2. 2. 2. 2. 2. 3.] [ 2. 2. 2. 2. 2. 3.] [ 3. 3. 3. 3. 3. 3.]]
źródło
W przypadku, gdy musisz dodać ogrodzenie 1s do tablicy:
>>> mat = np.zeros((4,4), np.int32) >>> mat array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) >>> mat[0,:] = mat[:,0] = mat[:,-1] = mat[-1,:] = 1 >>> mat array([[1, 1, 1, 1], [1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 1, 1]])
źródło
Wiem, że trochę się spóźniłem, ale na wypadek, gdybyś chciał wykonać względne wypełnienie (inaczej dopełnianie krawędzi), oto jak możesz to zaimplementować. Zwróć uwagę, że pierwsze wystąpienie przypisania powoduje wypełnienie zerami, więc możesz go użyć zarówno do wypełnienia zerami, jak i do wypełnienia względnego (w tym miejscu kopiujesz wartości krawędzi oryginalnej tablicy do wypełnionej tablicy).
def replicate_padding(arr): """Perform replicate padding on a numpy array.""" new_pad_shape = tuple(np.array(arr.shape) + 2) # 2 indicates the width + height to change, a (512, 512) image --> (514, 514) padded image. padded_array = np.zeros(new_pad_shape) #create an array of zeros with new dimensions # perform replication padded_array[1:-1,1:-1] = arr # result will be zero-pad padded_array[0,1:-1] = arr[0] # perform edge pad for top row padded_array[-1, 1:-1] = arr[-1] # edge pad for bottom row padded_array.T[0, 1:-1] = arr.T[0] # edge pad for first column padded_array.T[-1, 1:-1] = arr.T[-1] # edge pad for last column #at this point, all values except for the 4 corners should have been replicated padded_array[0][0] = arr[0][0] # top left corner padded_array[-1][0] = arr[-1][0] # bottom left corner padded_array[0][-1] = arr[0][-1] # top right corner padded_array[-1][-1] = arr[-1][-1] # bottom right corner return padded_array
Analiza złożoności:
Optymalnym rozwiązaniem jest metoda numpy'ego. Po uśrednieniu dla 5 przebiegów np.pad ze względnym wypełnieniem jest tylko
8%
lepszy niż funkcja zdefiniowana powyżej. To pokazuje, że jest to dość optymalna metoda wypełniania względnego i zerowego.#My method, replicate_padding start = time.time() padded = replicate_padding(input_image) end = time.time() delta0 = end - start #np.pad with edge padding start = time.time() padded = np.pad(input_image, 1, mode='edge') end = time.time() delta = end - start print(delta0) # np Output: 0.0008790493011474609 print(delta) # My Output: 0.0008130073547363281 print(100*((delta0-delta)/delta)) # Percent difference: 8.12316715542522%
źródło
Tensorflow zaimplementował również funkcje zmiany rozmiaru / wypełniania obrazów tf.image.pad tf.pad .
padded_image = tf.image.pad_to_bounding_box(image, top_padding, left_padding, target_height, target_width) padded_image = tf.pad(image, paddings, "CONSTANT")
Te funkcje działają tak samo jak inne funkcje potoku wprowadzania danych w tensorflow i będą działać znacznie lepiej w aplikacjach uczenia maszynowego.
źródło