Python obtiene promedio de vecinos en la matriz con valor na

Tengo una matriz muy grande, así que no quiero sumr yendo a través de cada fila y columna.

a = [[1,2,3],[3,4,5],[5,6,7]] def neighbors(i,j,a): return [a[i][j-1], a[i][(j+1)%len(a[0])], a[i-1][j], a[(i+1)%len(a)][j]] [[np.mean(neighbors(i,j,a)) for j in range(len(a[0]))] for i in range(len(a))] 

Este código funciona bien para 3×3 o un rango pequeño de matriz, pero esto no es factible para matrices grandes como 2k x 2k. Además, esto no funciona si falta alguno de los valores en la matriz o es como na

Este código funciona bien para 3×3 o un rango pequeño de matriz, pero esto no es factible para matrices grandes como 2k x 2k. Además, esto no funciona si falta alguno de los valores en la matriz o es como na . Si alguno de los valores del vecino es na , omita al vecino para obtener el promedio

Disparo # 1

Esto supone que usted está buscando obtener valores promedio de ventana deslizante en una matriz de entrada con una ventana de 3 x 3 y considerando solo los elementos del vecindario de noroeste a este.

Para tal caso, se podría usar signal.convolve2d con un kernel apropiado. Al final, debe dividir esas sums por el número de unidades en el kernel, es decir, kernel.sum() ya que solo las que contribuyeron a las sums. Aquí está la implementación:

 import numpy as np from scipy import signal # Inputs a = [[1,2,3],[3,4,5],[5,6,7],[4,8,9]] # Convert to numpy array arr = np.asarray(a,float) # Define kernel for convolution kernel = np.array([[0,1,0], [1,0,1], [0,1,0]]) # Perform 2D convolution with input data and kernel out = signal.convolve2d(arr, kernel, boundary='wrap', mode='same')/kernel.sum() 

Disparo # 2

Esto hace las mismas suposiciones que en el disparo # 1, excepto que estamos buscando encontrar valores promedio en una vecindad de solo cero elementos con la intención de reemplazarlos con esos valores promedio.

Enfoque n. ° 1: Esta es una forma de hacerlo utilizando un enfoque de convolución selectiva manual:

 import numpy as np # Convert to numpy array arr = np.asarray(a,float) # Pad around the input array to take care of boundary conditions arr_pad = np.lib.pad(arr, (1,1), 'wrap') R,C = np.where(arr==0) # Row, column indices for zero elements in input array N = arr_pad.shape[1] # Number of rows in input array offset = np.array([-N, -1, 1, N]) idx = np.ravel_multi_index((R+1,C+1),arr_pad.shape)[:,None] + offset arr_out = arr.copy() arr_out[R,C] = arr_pad.ravel()[idx].sum(1)/4 

Muestra de entrada, salida –

 In [587]: arr Out[587]: array([[ 4., 0., 3., 3., 3., 1., 3.], [ 2., 4., 0., 0., 4., 2., 1.], [ 0., 1., 1., 0., 1., 4., 3.], [ 0., 3., 0., 2., 3., 0., 1.]]) In [588]: arr_out Out[588]: array([[ 4. , 3.5 , 3. , 3. , 3. , 1. , 3. ], [ 2. , 4. , 2. , 1.75, 4. , 2. , 1. ], [ 1.5 , 1. , 1. , 1. , 1. , 4. , 3. ], [ 2. , 3. , 2.25, 2. , 3. , 2.25, 1. ]]) 

Para cuidar las condiciones de contorno, hay otras opciones para el relleno. Mira numpy.pad para más información.

Enfoque # 2: Esta sería una versión modificada del enfoque basado en convolución enumerado anteriormente en el Shot #1 . Esto es lo mismo que el enfoque anterior, excepto que al final, reemplazamos selectivamente los elementos cero con la salida de convolución. Aquí está el código –

 import numpy as np from scipy import signal # Inputs a = [[1,2,3],[3,4,5],[5,6,7],[4,8,9]] # Convert to numpy array arr = np.asarray(a,float) # Define kernel for convolution kernel = np.array([[0,1,0], [1,0,1], [0,1,0]]) # Perform 2D convolution with input data and kernel conv_out = signal.convolve2d(arr, kernel, boundary='wrap', mode='same')/kernel.sum() # Initialize output array as a copy of input array arr_out = arr.copy() # Setup a mask of zero elements in input array and # replace those in output array with the convolution output mask = arr==0 arr_out[mask] = conv_out[mask] 

Observaciones: el Approach #1 sería la forma preferida cuando tenga menos elementos de cero en la matriz de entrada, de lo contrario, elija el Approach #2 .

Este es un apéndice a los comentarios en la respuesta de @Divakar (en lugar de una respuesta independiente).

Por curiosidad probé diferentes ‘pseudo’ convoluciones en contra de la convolución escéptica. El más rápido fue el% (módulo) que lo envolvió, lo que me sorprendió: obviamente, el adormecimiento hace algo inteligente con su indexación, aunque obviamente no tener que rellenar ahorrará tiempo.

fn3 -> 9.5ms, fn1 -> 21ms, fn2 -> 232ms

 import timeit setup = """ import numpy as np from scipy import signal N = 1000 M = 750 P = 5 # ie small number -> bigger proportion of zeros a = np.random.randint(0, P, M * N).reshape(M, N) arr = np.asarray(a,float)""" fn1 = """ arr_pad = np.lib.pad(arr, (1,1), 'wrap') R,C = np.where(arr==0) N = arr_pad.shape[1] offset = np.array([-N, -1, 1, N]) idx = np.ravel_multi_index((R+1,C+1),arr_pad.shape)[:,None] + offset arr[R,C] = arr_pad.ravel()[idx].sum(1)/4""" fn2 = """ kernel = np.array([[0,1,0], [1,0,1], [0,1,0]]) conv_out = signal.convolve2d(arr, kernel, boundary='wrap', mode='same')/kernel.sum() mask = arr == 0.0 arr[mask] = conv_out[mask]""" fn3 = """ R,C = np.where(arr == 0.0) arr[R, C] = (arr[(R-1)%M,C] + arr[R,(C-1)%N] + arr[R,(C+1)%N] + arr[(R+1)%M,C]) / 4.0 """ print(timeit.timeit(fn1, setup, number = 100)) print(timeit.timeit(fn2, setup, number = 100)) print(timeit.timeit(fn3, setup, number = 100)) 

Usando numpy y scipy.ndimage , puede aplicar una “huella” que define dónde buscar a los vecinos de cada elemento y aplicar una función a esos vecinos:

 import numpy as np import scipy.ndimage as ndimage # Getting neighbours horizontally and vertically, # not diagonally footprint = np.array([[0,1,0], [1,0,1], [0,1,0]]) a = [[1,2,3],[3,4,5],[5,6,7]] # Need to make sure that dtype is float or the # mean won't be calculated correctly a_array = np.array(a, dtype=float) # Can specify that you want neighbour selection to # wrap around at the borders ndimage.generic_filter(a_array, np.mean, footprint=footprint, mode='wrap') Out[36]: array([[ 3.25, 3.5 , 3.75], [ 3.75, 4. , 4.25], [ 4.25, 4.5 , 4.75]])