How can I search grids within a numpy array? - python

I have a 100x100 grid of 1s and 0s, and I want to determine whether there are more than 4 0s within any given 3x3 on the 100x100 array. If less or equal this would return true, greater would return False. How can I do this?

You could also do something like this
from scipy.signal import convolve2d
window_size = 3
kernel = np.ones((window_size, window_size))
test_array = np.zeros((100, 100))
test_array[33:35, 33] = 1
test_array[34, 33:36] = 1
output = convolve2d(test_array, kernel, mode='valid')
threshold = 4
hits = np.nonzero(output >= threshold)
Where the "hits" are the top-left corners of the windows.

You could just do a brute force search
import numpy as np
grid = np.random.randint(0, 5, (100, 100))
summ = 0
searched = 0
for ii in range(100 - delta):
for jj in range(100 - delta):
searched += 1
if np.where(grid[ii:ii+delta, jj:jj+delta] == 0)[0].size > 4:
print('zeros found in (%i,%i) through (%i,%i) ' % (ii, jj, ii+delta, jj+delta))
summ += 1
print('number of sets of zeros > 4: %i/%i' % (summ, searched))

Related

Generating a random line on a 2d array in python

I've got a 2d array of zeros: 250 by 250. And I want to generate a random straight random line of a specific length (haven't yet decided). Obviously, since it's a line the values that turn from zero to one must be connected in some way, vertically, horizontally, or diagonally; and it also has to be straight. How could I do this? I'm quite stuck with this problem, any help would be appreciated.
We can do:
import numpy as np
SIZE = 250
arr = np.zeros((SIZE, SIZE))
M_POS = np.arange(-SIZE, SIZE)
M_POS = np.r_[M_POS, 1 / M_POS[M_POS!=0]]
M = np.random.choice(M_POS, 1)[0]
N = np.random.choice(np.arange(-SIZE, SIZE), 1)[0]
L = 50
P0 = np.array([0, N])
X_Y = np.array([1, 1 / M]) if abs(M) < 1 else np.array([1, M])
draw_in = np.add(np.repeat([P0], L, axis=0),
np.repeat([X_Y], L, axis=0) * np.arange(L)[:, np.newaxis]).astype(int)
draw_in = draw_in[((draw_in < SIZE) & (draw_in>0)).all(axis=1)]
arr[draw_in[:, 0], draw_in[:, 1]] = 1

np.where() to eliminate data, where coordinates are too close to each other

I'm doing aperture photometry on a cluster of stars, and to get easier detection of background signal, I want to only look at stars further apart than n pixels (n=16 in my case).
I have 2 arrays, xs and ys, with the x- and y-values of all the stars' coordinates:
Using np.where I'm supposed to find the indexes of all stars, where the distance to all other stars is >= n
So far, my method has been a for-loop
import numpy as np
# Lists of coordinates w. values between 0 and 2000 for 5000 stars
xs = np.random.rand(5000)*2000
ys = np.random.rand(5000)*2000
# for-loop, wherein the np.where statement in question is situated
n = 16
for i in range(len(xs)):
index = np.where( np.sqrt( pow(xs[i] - xs,2) + pow(ys[i] - ys,2)) >= n)
Due to the stars being clustered pretty closely together, I expected a severe reduction in data, though even when I tried n=1000 I still had around 4000 datapoints left
Using just numpy (and part of the answer here)
X = np.random.rand(5000,2) * 2000
XX = np.einsum('ij, ij ->i', X, X)
D_squared = XX[:, None] + XX - 2 * X.dot(X.T)
out = np.where(D_squared.min(axis = 0) > n**2)
Using scipy.spatial.pdist
from scipy.spatial import pdist, squareform
D_squared = squareform(pdist(x, metric = 'sqeuclidean'))
out = np.where(D_squared.min(axis = 0) > n**2)
Using a KDTree for maximum fast:
from scipy.spatial import KDTree
X_tree = KDTree(X)
in_radius = np.array(list(X_tree.query_pairs(n))).flatten()
out = np.where(~np.in1d(np.arange(X.shape[0]), in_radius))
np.random.seed(seed=1)
xs = np.random.rand(5000,1)*2000
ys = np.random.rand(5000,1)*2000
n = 16
mask = (xs>=0)
for i in range(len(xs)):
if mask[i]:
index = np.where( np.sqrt( pow(xs[i] - x,2) + pow(ys[i] - y,2)) <= n)
mask[index] = False
mask[i] = True
x = xs[mask]
y = ys[mask]
print(len(x))
4220
You can use np.subtract.outer for creating the pairwise comparisons. Then you check for each row whether the distance is below 16 for exactly one item (which is the comparison with the particular start itself):
distances = np.sqrt(
np.subtract.outer(xs, xs)**2
+ np.subtract.outer(ys, ys)**2
)
indices = np.nonzero(np.sum(distances < 16, axis=1) == 1)

Can I do a mathematical-shape mask for a (Nx,Ny) matrix?

I have a matrix of numbers (Nx,Ny) and I would like to select from that matrix a mathematical shape coordinates/components as it could be a line with a given slope.
I learned how to create a mask and how to do it in a random way but I cannot think of how to generate a mathematical-shape mask in python.
This is some of the code that I've been able to develop. If you know a better way to do this I will be also grateful of being told.
import random
import numpy as np
threshold = 0.85
radius=40
sq7=1/(radius*radius)
matrix=np.zeros((Nx,Ny))
for i in range(0,Nx):
for j in range(0,Ny):
if ((i-Nx*0.5)*(i-Nx*0.5)*sq7+(j-Ny*0.5)*(j-Ny*0.5)*sq7<=1.0):
matrix[i,j]= 1.0 - 0.1*random.random();
else:
matrix[i,j]=-1.0 + random.random();
randoms = np.random.normal(0,scale=0.002, size=matrix[mask].shape)
mask = matrix**2 < threshold
matrix[mask] += randoms * (1 - matrix[mask]**2)
At the end I found a very easy way of doing so. What I just did was to create a new matrix of the same dimensions as the one that I want to mask and then just by going through the matrix itself and comparing with the values of my function I could do it so easily. I will leave the code here.
def func_normaldist(x,Ny):
y = np.exp(-0.5*(x-int(Ny/2))**2)/np.sqrt(np.pi*2.)
return y
def mask_uvalues_centered_geometry(Nx, Ny): #u
mask = np.zeros((Nx,Ny))
# Initial configuration: rectangle of Nx x Ny
for j in range(0,Ny):
for i in range(0,Nx):
if (i < Ny*Nx*func_normaldist(j,Ny)):# and (i > int(Nx/2 + 1)):
mask[j,i] = True
else:
mask[j,i] = False;
return mask
Nx = 50
Ny = 50
a = mask_uvalues_centered_geometry(Nx,Ny)
print(a)

Place points with variable density

Assume that you have an NxM matrix, with values ranging from [0,100]. What I'd like to do is place points with a density (inversely) relative to the values in that area.
For example, here's a 2D Gaussian field, inverted s.t. the centroid has a value of 0, and the perimeter is at 100:
I'd like to pack the points so that they appear somewhat similar to this image:
Note how there is a radial spread outwards.
My attempt looks a little different :( ...
What I attempt to do is (i) generate a boolean area, of the same shape and size, and (ii) move through the rows and columns. If the value of the boolean array at some point is True, then pass; otherwise, add a [row,col] point to a list and cover the boolean array with True in a radius proportional to the value in the Gaussian array.
The choice of Gaussian for this example isn't important, the fundamental idea is that: given a floating point matrix, how can one place points with a density proportional to those values?
Any help very much appreciated :)
import matplotlib.pyplot as plt
import numpy as np
from math import exp
def gaussian(x,y,x0,y0,A=10.0,sigma_x=10.0,sigma_y=10.0):
return A - A*exp(-((x-x0)**2/(2*sigma_x**2) + (y-y0)**2/(2*sigma_y**2)))
def generate_grid(width=100,height=100):
grid = np.empty((width,height))
for x in range(0,width):
for y in range(0,height):
grid[x][y] = gaussian(x,y,width/2,height/2,A=100.0)
return grid
def cover_array(a,row,col,radius):
nRows = np.shape(grid)[0]
nCols = np.shape(grid)[1]
mid = round(radius / 2)
half_radius = int(round(radius))
for x in range(-half_radius,half_radius):
for y in range(-half_radius,half_radius):
if row+x >= 0 and x+row < nRows and col+y >= 0 and y+col < nCols:
if (x-mid)**2 + (y-mid)**2 <= radius**2:
a[row+x][col+y] = True
def pack_points(grid):
points = []
nRows = np.shape(grid)[0]
nCols = np.shape(grid)[1]
maxDist = 50.0
minDist = 0.0
maxEdge = 10.0
minEdge = 5.0
grid_min = 0.0
grid_max = 100.0
row = 0
col = 0
arrayCovered = np.zeros((nRows,nCols))
while True:
if row >= nRows:
return np.array(points)
if arrayCovered[row][col] == False:
radius = maxEdge * ((grid[row][col] - grid_min) / (grid_max - grid_min))
cover_array(arrayCovered,row,col,radius)
points.append([row,col])
col += 1
if col >= nCols:
row += 1
col = 0
grid = generate_grid()
plt.imshow(grid)
plt.show()
points = pack_points(grid)
plt.scatter(points[:,0],points[:,1])
plt.show()
Here is a cheap and simple method, although it requires hand-setting an amount parameter:
import numpy as np
import matplotlib.pyplot as plt
def gaussian(x,y,x0,y0,A=10.0,sigma_x=10.0,sigma_y=10.0):
return A - A*np.exp(-((x-x0)**2/(2*sigma_x**2) + (y-y0)**2/(2*sigma_y**2)))
def distribute_points(data, amount=1):
p = amount * (1 / data)
r = np.random.random(p.shape)
return np.where(p > r)
ii, jj = np.mgrid[-10:10:.1, -10:10:.1]
data = gaussian(ii, jj, 0, 0)
px, py = distribute_points(data, amount=.03)
plt.imshow(data)
plt.scatter(px, py, marker='.', c='#ff000080')
plt.xticks([])
plt.yticks([])
plt.xlim([0, len(ii)])
plt.ylim([0, len(jj)])
Result:

Turn grid into a checkerboard pattern in python?

I have successfully created a grid, but am now trying to turn my grid into a checkerboard pattern, preferably using a variant of the floodfill command. how do I make sure the program recognizes which squares are even and which are odd?
currently the IDE is set so m[i][j]= 1 gives blue, while m[i][j]= 0 gives red, which I am happy to keep, and so I do not need to define the colors. Thank you.
Code I have so far :
from pylab import *
from numpy import *
from math import *
m=zeros((100,100))
for i in range(100):
for j in range(100):
if (math.floor(i) % 10) != 0:
if (math.floor(j) % 10) != 0:
m[i][j]= 1
else:
m[i][j]= 0
imshow(m)
show()
Code output :
import numpy as np
def make_checkerboard(n_rows, n_columns, square_size):
n_rows_, n_columns_ = int(n_rows/square_size + 1), int(n_columns/square_size + 1)
rows_grid, columns_grid = np.meshgrid(range(n_rows_), range(n_columns_), indexing='ij')
high_res_checkerboard = np.mod(rows_grid, 2) + np.mod(columns_grid, 2) == 1
square = np.ones((square_size,square_size))
checkerboard = np.kron(high_res_checkerboard, square)[:n_rows,:n_columns]
return checkerboard
square_size = 5
n_rows = 14
n_columns = 67
checkerboard = make_checkerboard(n_rows, n_columns, square_size)
You can check the sum of the two indices (row and column) and color it with the first color if it's odd and second otherwise. Something like:
for i in range(nrows):
for j in range(ncols):
m[i][j] = 0 if (i+j)%2 else 1
Use the modulus operation:
m[i][j] = (i+j) % 2
I would create a linear array, fill every second value and reshape.
In your case (even amount of columns), prepend one column and get rid of it after reshaping:
import numpy as np
rows = 100
cols = 100 + 1 # product of rows*cols must be odd, we fix it later
m = np.zeros((rows*cols, 1)) # create array
m[::2] = 1 # fill every second
m = np.reshape(m, (rows, cols)) # reshape array to matrix
m = m[:, :-1] # cut additional column
You can create a checkerboard style array with NumPy, then resize it with scipy's imresize to make that equal to your desired canvas area.
Thus, the steps would be :
1) Create a NumPy array of shape (10,10) corresponding to 10 x 10 sized checkerboard pattern. To do so, start with zeros array and fill the alternate rows and columns with ones :
arr = np.zeros((10,10),dtype=int)
arr[::2,::2] = 1
arr[1::2,1::2] = 1
2) Resize the array 10x to have (100,100) pixel sized output image :
from scipy.misc import imresize # Importing required function
out = imresize(arr,10*np.array(arr.shape),interp='nearest')/255
Output :
With only minimal modification of your code it would look something like this:
from pylab import *
from numpy import *
from math import *
m=zeros((100,100))
for i in range(100):
for j in range(100):
if (math.floor(i) % 10) != 0:
if (math.floor(j) % 10) != 0:
m[i][j]= 1
if (int(i / 10) + int(j / 10)) % 2: # the only two extra lines.
m[i][j] = 0 #
imshow(m)
show()
Alternatively just this (assuming you really need the 100x100) to get rid of the "boundary lines":
m=zeros((100,100))
for i in range(100):
for j in range(100):
m[i][j] = (int(i / 10) + int(j / 10)) % 2
Cheers.

Categories