Copy upper triangle to lower triangle in a python matrix - python

iluropoda_melanoleuca bos_taurus callithrix_jacchus canis_familiaris
ailuropoda_melanoleuca 0 84.6 97.4 44
bos_taurus 0 0 97.4 84.6
callithrix_jacchus 0 0 0 97.4
canis_familiaris 0 0 0 0
This is a short version of the python matrix I have. I have the information in the upper triangle. Is there an easy function to copy the upper triangle to the down triangle of the matrix?

To do this in NumPy, without using a double loop, you can use tril_indices. Note that depending on your matrix size, this may be slower that adding the transpose and subtracting the diagonal though perhaps this method is more readable.
>>> i_lower = np.tril_indices(n, -1)
>>> matrix[i_lower] = matrix.T[i_lower] # make the matrix symmetric
Be careful that you do not try to mix tril_indices and triu_indices as they both use row major indexing, i.e., this does not work:
>>> i_upper = np.triu_indices(n, 1)
>>> i_lower = np.tril_indices(n, -1)
>>> matrix[i_lower] = matrix[i_upper] # make the matrix symmetric
>>> np.allclose(matrix.T, matrix)
False

The easiest AND FASTEST (no loop) way to do this for NumPy arrays is the following:
The following is ~3x faster for 100x100 matrices compared to the accepted answer and roughly the same speed for 10x10 matrices.
import numpy as np
X= np.array([[0., 2., 3.],
[0., 0., 6.],
[0., 0., 0.]])
X = X + X.T - np.diag(np.diag(X))
print(X)
#array([[0., 2., 3.],
# [2., 0., 6.],
# [3., 6., 0.]])
Note that the matrix must either be upper triangular to begin with or it should be made upper triangular as follows.
rng = np.random.RandomState(123)
X = rng.randomint(10, size=(3, 3))
print(X)
#array([[2, 2, 6],
# [1, 3, 9],
# [6, 1, 0]])
X = np.triu(X)
X = X + X.T - np.diag(np.diag(X))
print(X)
#array([[2, 2, 6],
# [2, 3, 9],
# [6, 9, 0]])

If I understand the question correctly, I believe this will work
for i in range(num_rows):
for j in range(i, num_cols):
matrix[j][i] = matrix[i][j]

Heres a better one i guess :
>>> a = np.arange(16).reshape(4, 4)
>>> print(a)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>> iu = np.triu_indices(4,1)
>>> il = (iu[1],iu[0])
>>> a[il]=a[iu]
>>> a
array([[ 0, 1, 2, 3],
[ 1, 5, 6, 7],
[ 2, 6, 10, 11],
[ 3, 7, 11, 15]])

If U is an upper triangular matrix, you can use triu and transpose to make it symmetric:
LDU = triu(U,1)+U.T

def inmatrix(m,n):#input Matrix Function
a=[]
for i in range(m):
b=[]
for j in range(n):
elm=int(input("Enter number in Pocket ["+str(i)+"]["+str(j)+"] "))
b.append(elm)
a.append(b)
return a
def Matrix(a):#print Matrix Function
for i in range(len(a)):
for j in range(len(a[0])):
print(a[i][j],end=" ")
print()
m=int(input("Enter number of row "))
n=int(input("Enter number of column"))
a=inmatrix(m,n) #call input Matrix function
Matrix(a)#print Matrix
t=[]#create Blank list
for i in range(m):
for j in range(n):
if i>j:#check upper triangular Elements
t.append(a[i][j])#add them in a list
k=0#variable for list
for i in range(m):
for j in range(n):
if i<j:
a[i][j]=t[k]copy list item to lower triangular
k=k+1
Matrix(a)# print Matrix after change

Related

NumPy: How to make a 2-dim data array containing both x inputs and f(x) outputs (using example of kinematics)

Trying to make a simple kinematics array (for fun!) where
You're prompted for an int input tmax that determines t = [0, tmax]
So this would mean that if you input tmax = 5, the time interval would be t = [0, 5] seconds
You're prompted for a float input jerkc (third derivative of position) that determines the constant used in calculation of acceleration (jerkc * t), velocity (jerkc * 1/2 * t^2) and position.
I'm trying to use NumPy (I'm very new to this, and to python) to create a two-dimensional array where:
Row 1: time (so for tmax = 5 this row would be: [0, 1, 2, 3, 4, 5])
Row 2: acceleration(t) (so for tmax = 5, Jerkc = 2 this row would be [0, 2, 4, 6, 8, 10]
Row 3: velocity(t) (so on...)
Row 4: position(t) (so on.....)
I'm still very unfamiliar with lists - and especially NumPy arrays. I'm used to Java arrays, if you're wondering why I'm treating this array the way I am. Here's a sample of what I have so far:
import numpy as np
tmax = int(input('Please enter a timeframe [0, t] [int]: '))
print('Thank you! Your timeframe will be [0, ', tmax, ']!')
jerkc = float(input('Please enter a jerk constant [float]: '))
print('Thank you! Your jerk constant will be ', jerkc, '!')
physics = np.array([[], [], [], []])
t = int(0)
i = int(0)
m = int(0)
e = int(0)
while (t <= tmax):
physics[0, t] = t
t = t + 1
while (i <= tmax):
physics[1, t] = (jerkc * t)
t = t + 1
while (m <= tmax):
physics[2, t] = ((jerkc) * (1/2) * (t ^ 2))
t = t + 1
while (e <= tmax):
physics[3, t] = ((jerkc) * (1/2) * (1/3) * (t ^ 3))
print(physics)
If anyone knows what I'm doing wrong, or can explain to me a way I can use arrays better, please let me know, and please explain yourself carefully and understandably! Thank you!
The special thing about NumPy arrays is that you can do calculations with it. This means that the if you multiply 2 arrays, the result array will contain the values of those arrays multiplied with eachother.
As in this example:
>>> array1=array([1,2,3,4])
>>> array2=array([5,6,7,8])
>>>
>>> array1 * array2
array([ 5, 12, 21, 32])
This does also work for addition, substraction, division...
Another great thing in NumPy is the arange function. It works like the range function in python, but instead returns an array. It has three arguments: start, end and step. The returned array will start with the start argument. The following item will then be added up with the step value, and so on. Untill the next item would be larger then the end value.
Here's an example:
>>> arange(1,10)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> arange(1,10,2)
array([1, 3, 5, 7, 9])
>>> arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Note: When the step argument is not provided, it is defaulted to 1. If you provide only one argument, it will start from 0 and end with this argument.
There's also a zeros function. It returns an array of the given size, filled with zeros. It is preferrable to initialise arrays in this way.
>>> zeros(10)
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
>>> zeros((2,2))
array([[0., 0.],
[0., 0.]])
Finally, just as almost all indexable objects in python, NumPy arrays support slicing. It works in the way array[start:end]. It returns the part of the array from index start to index before end. If the start is omitted, the result will be from the start of the array to index end. The same goes for the end argument. If both are omitted (:), the result is just the whole array. Additionally, a third step value can be provided, array[start:end:step]. This does works in the same way as with arange, the indexes of the result values will be counted up from start.
An example:
>>> array1=array([1,2,3,4,5,6,7,8,9,10])
>>>
>>> array1[2:4]
array([3, 4])
>>> array1[:4]
array([1, 2, 3, 4])
>>> array1[2:]
array([ 3, 4, 5, 6, 7, 8, 9, 10])
>>> array1[:]
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> array1[5:9:3] #with step value
array([6, 9])
As NumPy prefers having an index for all the dimensions, we are going to use physics[1,:] to tell NumPy that we want to change all values in the array.
If we would rewrite your code using the previously mentioned things, it would look like:
import numpy as np
tmax = int(input('Please enter a timeframe [0, t] [int]: '))
print('Thank you! Your timeframe will be [0, ', tmax, ']!')
jerkc = float(input('Please enter a jerk constant [float]: '))
print('Thank you! Your jerk constant will be ', jerkc, '!')
physics = np.zeros((4,tmax+1))
physics[0, :] = np.arange(tmax+1)#+1, as the end is not included in the array.
physics[1, :] = physics[0, :] * jerkc
physics[2, :] = ((jerkc) * (1/2) * (physics[0, :] ** 2))
physics[3, :] = ((jerkc) * (1/2) * (1/3) * (physics[0, :] ** 3))
Note: ** is the power operator in python, ^ is exclusive or and thus won't work.

How to invert volume of matrices in numpy?

Please assume a vector of invertible matrices:
import numpy as np
a = np.arange(120).reshape((2, 2, 5, 6))
I want to invert the matrices over their defined axes:
b = np.linalg.inv(a, axis1=0, axis2=1)
but this does not seems supported.
How to achieve this?
inv docs specifies its array input as:
a : (..., M, M) array_like
Matrix to be inverted.
You have a
a = np.arange(120).reshape((2, 2, 5, 6))
(M,M,...)
The dimensions are in the wrong order - change them!
In [44]: a = np.arange(120).reshape((2, 2, 5, 6))
Change the axes to the order that inv accepts:
In [45]: A = a.transpose(2,3,0,1)
In [46]: Ai = np.linalg.inv(A)
In [47]: Ai.shape
Out[47]: (5, 6, 2, 2)
In [48]: ai = Ai.transpose(2,3,0,1) # and back
In [49]: ai.shape
Out[49]: (2, 2, 5, 6)
I was going to test the result, but got:
In [50]: x = a#ai
Traceback (most recent call last):
File "<ipython-input-50-9dfe3616745d>", line 1, in <module>
x = a#ai
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 5 is different from 6)
Like inv, matmul treats the last 2 dimensions as the matrix, the first 2 as 'batch':
In [51]: x = A#Ai
In [52]: x[0,0]
Out[52]:
array([[1., 0.],
[0., 1.]])
In [53]: x[0,3]
Out[53]:
array([[1.00000000e+00, 1.38777878e-17],
[4.44089210e-16, 1.00000000e+00]])
We can do the equivalent with einsum:
In [55]: x = np.einsum('ijkl,jmkl->imkl',a,ai)
In [56]: x[:,:,0,0]
Out[56]:
array([[1., 0.],
[0., 1.]])
You might want to change the original specification to match the inv and matmul usage. It could make life easier for you. Also remember that in numpy the trailing dimensions are the inner most ones.
If you know that the matrices are 2x2 you can do that easily using the standard formula for inverting such matrices; otherwise, I fear the only reasonable solution would be to do it with for loops? For example, the following works for any shape (modifying the sizes adequately):
b = np.stack([np.linalg.inv(a[:, :, i, j]) for i in range(a.shape[2]) for j in range(a.shape[3])], axis=2)
b = b.reshape(2, 2, 5, 6)
as checked by
for i in range(a.shape[2]):
for j in range(a.shape[3]):
assert np.allclose(np.dot(a[:,:,i,j], b[:,:,i,j]), np.eye(2))
In the specific 2x2 case you can do the following, which is fully vectorized hence probably faster:
determinants = a[0, 0] * a[1, 1] - a[0, 1] * a[1, 0]
b = 1 / determinants * np.stack([
np.stack([a[1, 1], -a[0, 1]]),
np.stack([-a[1, 0], a[0, 0]]),
])
On the specific (small) input size, the second solution is about 10 times faster in my tests (43us vs. 537us).

Vectorising the padding of ragged arrays [duplicate]

consider the list of lists l
l = [[1, 2, 3], [1, 2]]
if I convert this to a np.array I'll get a one dimensional object array with [1, 2, 3] in the first position and [1, 2] in the second position.
print(np.array(l))
[[1, 2, 3] [1, 2]]
I want this instead
print(np.array([[1, 2, 3], [1, 2, np.nan]]))
[[ 1. 2. 3.]
[ 1. 2. nan]]
I can do this with a loop, but we all know how unpopular loops are
def box_pir(l):
lengths = [i for i in map(len, l)]
shape = (len(l), max(lengths))
a = np.full(shape, np.nan)
for i, r in enumerate(l):
a[i, :lengths[i]] = r
return a
print(box_pir(l))
[[ 1. 2. 3.]
[ 1. 2. nan]]
how do I do this in a fast, vectorized way?
timing
setup functions
%%cython
import numpy as np
def box_pir_cython(l):
lengths = [len(item) for item in l]
shape = (len(l), max(lengths))
a = np.full(shape, np.nan)
for i, r in enumerate(l):
a[i, :lengths[i]] = r
return a
def box_divikar(v):
lens = np.array([len(item) for item in v])
mask = lens[:,None] > np.arange(lens.max())
out = np.full(mask.shape, np.nan)
out[mask] = np.concatenate(v)
return out
def box_hpaulj(LoL):
return np.array(list(zip_longest(*LoL, fillvalue=np.nan))).T
def box_simon(LoL):
max_len = len(max(LoL, key=len))
return np.array([x + [np.nan]*(max_len-len(x)) for x in LoL])
def box_dawg(LoL):
cols=len(max(LoL, key=len))
rows=len(LoL)
AoA=np.empty((rows,cols, ))
AoA.fill(np.nan)
for idx in range(rows):
AoA[idx,0:len(LoL[idx])]=LoL[idx]
return AoA
def box_pir(l):
lengths = [len(item) for item in l]
shape = (len(l), max(lengths))
a = np.full(shape, np.nan)
for i, r in enumerate(l):
a[i, :lengths[i]] = r
return a
def box_pandas(l):
return pd.DataFrame(l).values
This seems to be a close one of this question, where the padding was with zeros instead of NaNs. Interesting approaches were posted there, along with mine based on broadcasting and boolean-indexing. So, I would just modify one line from my post there to solve this case like so -
def boolean_indexing(v, fillval=np.nan):
lens = np.array([len(item) for item in v])
mask = lens[:,None] > np.arange(lens.max())
out = np.full(mask.shape,fillval)
out[mask] = np.concatenate(v)
return out
Sample run -
In [32]: l
Out[32]: [[1, 2, 3], [1, 2], [3, 8, 9, 7, 3]]
In [33]: boolean_indexing(l)
Out[33]:
array([[ 1., 2., 3., nan, nan],
[ 1., 2., nan, nan, nan],
[ 3., 8., 9., 7., 3.]])
In [34]: boolean_indexing(l,-1)
Out[34]:
array([[ 1, 2, 3, -1, -1],
[ 1, 2, -1, -1, -1],
[ 3, 8, 9, 7, 3]])
I have posted few runtime results there for all the posted approaches on that Q&A, which could be useful.
Probably the fastest list version uses itertools.zip_longest (may be izip_longest in Py2):
In [747]: np.array(list(itertools.zip_longest(*ll,fillvalue=np.nan))).T
Out[747]:
array([[ 1., 2., 3.],
[ 1., 2., nan]])
The plain zip produces:
In [748]: list(itertools.zip_longest(*ll))
Out[748]: [(1, 1), (2, 2), (3, None)]
another zip 'transposes':
In [751]: list(zip(*itertools.zip_longest(*ll)))
Out[751]: [(1, 2, 3), (1, 2, None)]
Often when starting with lists (or even an object array of lists), it is faster to stick with list methods. There's an substantial overhead in creating an array or dataframe.
This isn't the first time this question has been asked.
How can I pad and/or truncate a vector to a specified length using numpy?
My answer there includes both this zip_longest and your box_pir
I think there's also a fast numpy version using a flattened array, but I don't recall the details. It was probably given by Warren or Divakar.
I think the 'flattened' version works something along this line:
In [809]: ll
Out[809]: [[1, 2, 3], [1, 2]]
In [810]: sll=np.hstack(ll) # all values in a 1d array
In [816]: res=np.empty((2,3)); res.fill(np.nan) # empty target
get flattened indices where values go. This is the crucial step. Here the use of r_ is iterative; the fast version probably uses cumsum
In [817]: idx=np.r_[0:3, 3:3+2]
In [818]: idx
Out[818]: array([0, 1, 2, 3, 4])
In [819]: res.flat[idx]=sll
In [820]: res
Out[820]:
array([[ 1., 2., 3.],
[ 1., 2., nan]])
================
so the missing link is >np.arange() broadcasting
In [897]: lens=np.array([len(i) for i in ll])
In [898]: mask=lens[:,None]>np.arange(lens.max())
In [899]: mask
Out[899]:
array([[ True, True, True],
[ True, True, False]], dtype=bool)
In [900]: idx=np.where(mask.ravel())
In [901]: idx
Out[901]: (array([0, 1, 2, 3, 4], dtype=int32),)
I might write this as a form of slice assignment on each of the sub arrays that have been filled with a default:
def to_numpy(LoL, default=np.nan):
cols=len(max(LoL, key=len))
rows=len(LoL)
AoA=np.empty((rows,cols, ))
AoA.fill(default)
for idx in range(rows):
AoA[idx,0:len(LoL[idx])]=LoL[idx]
return AoA
I added in Divakar's Boolean Indexing as f4 and added to the timing testing. At least on my testing, (Python 2.7 and Python 3.5; Numpy 1.11) it is not the fastest.
Timing shows that izip_longest or f2 is slightly faster for most lists but slice assignment (which is f1) is faster for larger lists:
from __future__ import print_function
import numpy as np
try:
from itertools import izip_longest as zip_longest
except ImportError:
from itertools import zip_longest
def f1(LoL):
cols=len(max(LoL, key=len))
rows=len(LoL)
AoA=np.empty((rows,cols, ))
AoA.fill(np.nan)
for idx in range(rows):
AoA[idx,0:len(LoL[idx])]=LoL[idx]
return AoA
def f2(LoL):
return np.array(list(zip_longest(*LoL,fillvalue=np.nan))).T
def f3(LoL):
max_len = len(max(LoL, key=len))
return np.array([x + [np.nan]*(max_len-len(x)) for x in LoL])
def f4(LoL):
lens = np.array([len(item) for item in LoL])
mask = lens[:,None] > np.arange(lens.max())
out = np.full(mask.shape,np.nan)
out[mask] = np.concatenate(LoL)
return out
if __name__=='__main__':
import timeit
for case, LoL in (('small', [list(range(20)), list(range(30))] * 1000),
('medium', [list(range(20)), list(range(30))] * 10000),
('big', [list(range(20)), list(range(30))] * 100000),
('huge', [list(range(20)), list(range(30))] * 1000000)):
print(case)
for f in (f1, f2, f3, f4):
print(" ",f.__name__, timeit.timeit("f(LoL)", setup="from __main__ import f, LoL", number=100) )
Prints:
small
f1 0.245459079742
f2 0.209980010986
f3 0.350691080093
f4 0.332141160965
medium
f1 2.45869493484
f2 2.32307982445
f3 3.65722203255
f4 3.55545687675
big
f1 25.8796288967
f2 26.6177148819
f3 41.6916451454
f4 41.3140149117
huge
f1 262.429639101
f2 295.129109859
f3 427.606887817
f4 441.810388088
Maybe something like this? Don't know about your hardware, but means at 16ms for 100 loops for l2 = [list(range(20)), list(range(30))] * 10000.
from numpy import nan
def box(l):
max_lenght = len(max(l, key=len))
return [x + [nan]*(max_lenght-len(x)) for x in l]
If this is only for a 2D list, this might be your answer:
from numpy import nan
def even(data):
maxlen = max(len(l) for l in data)
for l in data:
l.extend([nan] * (maxlen - len(l)))
And if you don't want to modify the actual list:
from numpy import nan
def even(data):
res = data.copy()
maxlen = max(len(l) for l in res)
for l in res:
l.extend([nan] * (maxlen - len(l)))
return res

Fast way to take average of every N rows in a .npy array

I have a very large masked NumPy array (originalArray) with many rows and two columns. I want take the average of every two rows in originalArray and build a newArray in which each row is the average of two rows in originalArray (so newArray has half as many rows as originalArray). This should be a simple thing to do, but the script below is EXTREMELY slow. Any advice from the community would be greatly appreciated.
newList = []
for i in range(0, originalArray.shape[0], 2):
r = originalArray[i:i+2,:].mean(axis=0)
newList.append(r)
newArray = np.asarray(newList)
There must be a more elegant way of doing this. Many thanks!
The mean of two values a and b is 0.5*(a+b)
Therefore you can do it like this:
newArray = 0.5*(originalArray[0::2] + originalArray[1::2])
It will sum up all two consecutive rows and in the end multiply every element by 0.5.
Since in the title you are asking for avg over N rows, here is a more general solution:
def groupedAvg(myArray, N=2):
result = np.cumsum(myArray, 0)[N-1::N]/float(N)
result[1:] = result[1:] - result[:-1]
return result
The general form of the average over n elements is sum([x1,x2,...,xn])/n.
The sum of elements m to m+n in vector v is the same as subtracting the m-1th element from the m+nth element of cumsum(v). Unless m is 0, in that case you don't subtract anything (result[0]).
That is what we take advantage of here. Also since everything is linear, it is not important where we divide by N, so we do it right at the beginning, but that is just a matter of taste.
If the last group has less than N elements, it will be ignored completely.
If you don't want to ignore it, you have to treat the last group specially:
def avg(myArray, N=2):
cum = np.cumsum(myArray,0)
result = cum[N-1::N]/float(N)
result[1:] = result[1:] - result[:-1]
remainder = myArray.shape[0] % N
if remainder != 0:
if remainder < myArray.shape[0]:
lastAvg = (cum[-1]-cum[-1-remainder])/float(remainder)
else:
lastAvg = cum[-1]/float(remainder)
result = np.vstack([result, lastAvg])
return result
Your problem (average of every two rows with two columns):
>>> a = np.reshape(np.arange(12),(6,2))
>>> a
array([[ 0, 1],
[ 2, 3],
[ 4, 5],
[ 6, 7],
[ 8, 9],
[10, 11]])
>>> a.transpose().reshape(-1,2).mean(1).reshape(2,-1).transpose()
array([[ 1., 2.],
[ 5., 6.],
[ 9., 10.]])
Other dimensions (average of every four rows with three columns):
>>> a = np.reshape(np.arange(24),(8,3))
>>> a
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17],
[18, 19, 20],
[21, 22, 23]])
>>> a.transpose().reshape(-1,4).mean(1).reshape(3,-1).transpose()
array([[ 4.5, 5.5, 6.5],
[ 16.5, 17.5, 18.5]])
General formula for taking the average of r rows for a 2D array a with c columns:
a.transpose().reshape(-1,r).mean(1).reshape(c,-1).transpose()
import numpy as np
def av(array):
return 1. * np.sum(array.reshape(1. * array.shape[0] / 2,2, array.shape[1]),axis = 1) / array.shape[1]
a = np.array([[1,1],[2,2],[3,3],[4,4]])
print av(a)
>> [[ 1.5 1.5] [ 3.5 3.5]]

How to find neighbors of a 2D list in python?

I have a 2D list of only 1's and 0's:
Boundaries = [
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0]]
I need to test this list to check if there are any 1's surrounded by 8 other 1's (such as the middle 1 in this list). If there is a 1 surrounded by 1's as neighbours it should then be changed into a 0 such that after running the program the list above would return as something like this:
[
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,0,1,1],
[0,1,1,1,0],
[0,0,1,0,0]]
I'm trying to only use one parameter (the matrix of 1's and 0's). For some reason this is an incredibly difficult thing to wrap my head around. So far my code looks something like this:
def tempBoundaries(matrixC):
for i in matrixC:
for j in i:
if j == 1:
try:
if matrixC[i-1]==1 or matrixC[i+1]==1:
.......
This is a real struggle for whatever reason and I seem to be incapable of figuring out what to do, any tips or help would be greatly appreciated! Thanks.
Using scipy you'd do something like the following
import numpy
boundaries = numpy.array([
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0]])
counts = scipy.signal.convolve2d(boundaries, numpy.ones((3,3)), mode='same')
# which gives you a matrix with counts of the number of 1s around each point
array([[ 1., 2., 3., 2., 1.],
[ 2., 4., 6., 5., 3.],
[ 3., 6., 9., 7., 4.],
[ 2., 5., 7., 6., 3.],
[ 1., 3., 4., 3., 1.]])
# so then you just find the points where it's == 9
counts == 9
array([[False, False, False, False, False],
[False, False, False, False, False],
[False, False, True, False, False],
[False, False, False, False, False],
[False, False, False, False, False]], dtype=bool)
# so you can update those positions
boundaries[counts == 9] = 0
So the whole operation is simply:
boundaries = numpy.array(Boundaries)
counts = scipy.signal.convolve2d(boundaries, numpy.ones((3,3)), mode='same')
boundaries[counts == 9] = 0
Since the no numpy requirement was added later I feel I should add a pure python answer.
You could flip the algorithm around. This answer is inspired by the Hough Transform that's used in computer vision feature extraction (http://en.wikipedia.org/wiki/Hough_transform). Instead of hunting around a position you let positions vote for the things they influence. In your case each position with a 1 in it votes for itself and all its neighbours.
It's a little bit of a different approach but it simplifies the logic around hitting the edges of the data. You can just ignore that aspect because even though, for example, (-1, 0) gets voted for, it won't get enough votes to be considered.
Updated
Changed so that a cell doesn't vote for itself. This allows us to use it in the other case too (by searching for cells that have 8 votes). I've split it in to a function that finds all the cells that are surrounded by 1s and an operation that does the flipping (depending on what you're searching for).
import collections
import itertools
def neighbours_of(i, j):
"""Positions of neighbours (includes out of bounds but excludes cell itself)."""
neighbours = list(itertools.product(range(i-1, i+2), range(j-1, j+2)))
neighbours.remove((i, j))
return neighbours
def find_surrounded(grid):
"""List of x,y positions in grid where the cell is surrounded by 1s."""
votes = collections.defaultdict(int)
for i, x in enumerate(grid):
for j, y in enumerate(x):
# we don't get to vote if not ...
if y == 0:
continue
# vote for everyone in the 3x3 square around us
for a, b in neighbours_of(i, j):
votes[(a, b)] += 1
# now the things we want to change are those that got 8 votes
surrounded_positions = [pos for pos, count in votes.items() if count == 8]
return surrounded_positions
def change_when_cell_type_surrounded(grid, cell_type):
"""Update grid inline to flip bits of cells of cell_type that are surrounded."""
# we'll flip to the opposite of what we're looking for
change_to = 1 - cell_type
surrounded = find_surrounded(grid)
for i, j in surrounded:
if grid[i][j] == cell_type:
grid[i][j] = change_to
grid = [[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0]]
change_when_cell_type_surrounded(grid, 1)
change_when_cell_type_surrounded(grid, 0)
To simplify your code, you should move the code to check the neighbors inside a function. You can also use a list of directions, then iterate through the directions, like this:
directions = [(-1, -1), (0, -1), ...]
def check_neighbors(m, x, y):
for direction in directions:
dx, dy = direction
# You should check here that (x+dx, y+dx)
# is still inside the matrix
if ...:
continue
if matrix[x+dx][y+dy] == 0:
return False
return True
In your main function, your matrix is basically a list of lists. Since you're going to manipulate the indexes, you should use a range of the possible indexes.
# Big assumption: all sublists have the same length
for x in range(len(matrixC)):
for y in range(len(matrixC[0])):
if check_neighbors(matrixC, x, y):
# Do what you want here
...
what about this:
Boundaries = [
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0]]
tochange = []
for i in xrange(len(Boundaries)-3):
for j in xrange(len(Boundaries)-3):
for k in xrange(3):
for l in xrange(3):
if not Boundaries[i+k][j+k]:
break
else:
continue
break
else:
tochange.append((i+1, j+1))
for i, j in tochange:
Boundaries[i][j] = 0
You can use numpy
from numpy import ones
from scipy.signal import convolve2d
kernel = ones((3, 3))
#you create a matrix like this
#1 1 1
#1 1 1
#1 1 1
image = array(Boundaries)
#your boundaries were converted to a bidimensional numpy array
convolved = convolve2d(image, kernel, mode="same")
#you will have an image like this:
#[
# [1,2,3,2,1],
# [2,4,6,4,3],
# [3,6,9,7,4],
# [2,4,6,4,3],
# [1,2,3,2,1]
#]
then you should change the images accordingly:
for (x,y), value in numpy.ndenumerate(a):
image[x, y] = image[x, y] if convolved[x, y] < 9 else 0
disclaimer : this code does not kill 1s in the borders. you must change it accordingly
No numpy
Edit
I think this is a better solution, as it breaks the inner loop as soon as a neighbor is
equal to zero
def zero_one(input2d, r, c):
for rr in (r-1, r, r+1):
for cc in (c-1, c, c+1):
if input2d[rr][cc] == 0 : return 1
return 0
Boundaries = [[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0]]
rows = 5
cols = 5
Output = []
for r in range(rows):
Output.append([])
for c in range(cols):
if (r==0 or r==rows-1) or (c==0 or c==cols-1):
Output[r].append(Boundaries[r][c])
elif Boundaries[r][c] == 0:
Output[r].append(0)
else:
Output[r].append(zero_one(Boundaries, r, c))
for line in Output:
print line
executing the above code gives
[0, 0, 0, 0, 0]
[0, 1, 1, 1, 0]
[0, 1, 0, 1, 1]
[0, 1, 1, 1, 0]
[0, 0, 1, 0, 0]
My previous code is after the
End of Edit
In [15]: Boundaries = [
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0]]
In [16]: Output = [
[0,0,0,0,0],
[0,1,1,1,0],
[0,1,1,1,1],
[0,1,1,1,0],
[0,0,1,0,0]]
In [17]: for i in (1, 2, 3):
for j in (1,2,3):
s = 0
for m in (i-1, i, i+1):
for n in (j-1, j, j+1):
s = s+Boundaries[n][m]
if s == 9 : Output[j][i] = 0
....:
In [18]: Output
Out[18]:
[[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 1, 0, 1, 1],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0]]
In [19]:
I created a simple one like this, you can make custom
from itertools import product, starmap, islice
def findNeighbors(grid, x, y):
xi = (0, -1, 1) if 0 < x < len(grid) - 1 else ((0, -1) if x > 0 else (0, 1))
yi = (0, -1, 1) if 0 < y < len(grid[0]) - 1 else ((0, -1) if y > 0 else (0, 1))
return islice(starmap((lambda a, b: grid[x + a][y + b]), product(xi, yi)), 1, None)
1st example:
grid = [[ 0, 1, 2, 3],
... [ 4, 5, 6, 7],
... [ 8, 9, 10, 11],
... [12, 13, 14, 15]]
n = list(findNeighbors(grid, 2, 1)) # find neighbors of 9
print(n)
Output:
[8, 10, 5, 4, 6, 13, 12, 14]
2nd Example:
grid = [[ 0, 1, 2, 3],
... [ 4, 5, 6, 7],
... [ 8, 9, 10, 11],
... [12, 13, 14, 15]]
n1 = list(findNeighbors(grid, 3, 3)) # find neighbors of 15
print(n1)
Output:
[14, 11, 10]
Let me know if you have any question

Categories

Resources