so the assignment is:
write a func that gets N ( int greater than 0 ) and return a 2d array of 1's where a path of 0 makes a diamond. the length of each diamond line(1 of 4 lines) is of length N. cannot use loops list comps or recursion.
i will add my own solution but I don't think it is very numpy oriented..
please note that my solution was originally for a diamond of 1's in a 0 matrix so it might look a bit odd
example:
n=1
np.array([[0]])
n=2:
np.array([[1,0,1],[0,1,0],[1,0,1]])
n=3
np.array([[1,1,0,1,1],[1,0,1,0,1],[0,1,1,1,0],[1,0,1,0,1],[1,1,0,1,1]])
My Solution:
if n == 1:
finalMatrix = np.array([[0]])
return finalMatrix
rawMatrix = np.ones((2 * n - 1, 2 * n - 1))
midRowDiag = np.eye(2 * n - 1, k=n - 1)
midColDiag = np.eye(2 * n - 1, k=-(n - 1))
tempMatrix = rawMatrix - midRowDiag - midColDiag
halfMatrix = tempMatrix[::, n:]
# print(halfMatrix)
# Making the quarters
topRightQuart = halfMatrix[:n - 1, 0:n - 1]
topLeftQuart = np.fliplr(topRightQuart)
botLeftQuart = np.flipud(topLeftQuart)
botRightQuart = np.flipud(topRightQuart)
midCol = tempMatrix[:, n - 1]
midRow = tempMatrix[n - 1, :]
# Adding them all up
finalMatrix = np.zeros((2 * n - 1, 2 * n - 1))
finalMatrix[:, n - 1] += midCol
finalMatrix[n - 1, :] += midRow
finalMatrix[0:n - 1, 0:n - 1] += topLeftQuart
finalMatrix[0:n - 1, n:] += topRightQuart
finalMatrix[n:, 0:n - 1] += botLeftQuart
finalMatrix[n:, n:] += botRightQuart
finalMatrix[n - 1, n - 1] = 1
return finalMatrix
arr = np.diagflat([1,1],1)
arr = np.maximum(arr,np.flip(arr,0))
arr = np.maximum(arr,np.flip(arr,1))
arr = np.where((arr==0)|(arr==1), arr^1, arr)
As per my understanding you are looking to make diamonds write. This is an example for 3x3 one for 5x5 the code is underneath
arr = np.diagflat([1,1,1],2)
arr = np.maximum(arr,np.flip(arr,0))
arr = np.maximum(arr,np.flip(arr,1))
arr = np.where((arr==0)|(arr==1), arr^1, arr)
so for n needs to be a odd number and n=odd number
list of 1 len = n//2+1 and the second parameter in np.diagflat will be n//2. This is important to use // or you can simply divide and round it off
if n is the length of diagonal then the second parameter will be n-1
This pattern looks similar to a Toeplitz matrix. You can implement this using scipy.linalg.toeplitz:
>>> p = np.array([1,1,0,1,1])
>>> T = toeplitz(p, p)
Notice the layout of T:
array([[1, 1, 0, 1, 1],
[1, 1, 1, 0, 1],
[0, 1, 1, 1, 0],
[1, 0, 1, 1, 1],
[1, 1, 0, 1, 1]])
We can horizontally flip it (np.fliplr) and point-wise multiply with itself:
>>> T*np.fliplr(T)
array([[1, 1, 0, 1, 1],
[1, 0, 1, 0, 1],
[0, 1, 1, 1, 0],
[1, 0, 1, 0, 1],
[1, 1, 0, 1, 1]])
Minimal implementation:
def diamond(n):
pad = (1,)*(n-1)
p = np.array(pad + (0,) + pad)
T = toeplitz(p, p)
return np.fliplr(T)*T
Then:
>>> diamond(1)
array([[0]])
>>> diamond(2)
array([[1, 0, 1],
[0, 1, 0],
[1, 0, 1]])
>>> diamond(3)
array([[1, 1, 0, 1, 1],
[1, 0, 1, 0, 1],
[0, 1, 1, 1, 0],
[1, 0, 1, 0, 1],
[1, 1, 0, 1, 1]])
Related
LIST OF LIST BIN DIVIDED INTO 8 : [[0, 1, 1, 0, 0, 1, 0, 1], [0, 1, 1, 1, 0, 1, 1, 1]]
the output I want is:
[101, 119]
This is more complex but significantly faster than any kind of string manipulation as it's essentially just integer arithmetic.
from timeit import timeit
lob = [[0, 1, 1, 0, 0, 1, 0, 1], [0, 1, 1, 1, 0, 1, 1, 1]]
def v1():
result = []
for e in lob:
r = 0
for _e in e:
r = r * 2 + _e
result.append(r)
return result
def v2():
return [int(''.join([str(y) for y in x]), 2) for x in lob]
assert v1() == v2()
for func in v1, v2:
print(func.__name__, timeit(func))
Output:
v1 0.6906622060014342
v2 2.173182999999881
I have a list:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
I want to change this to:
result = [[0, 0], [1, 2], [4, 5]]
How to generate:
array: [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
map: 0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
# start to end, generate the result like `[int(start), int(end)]`
combine them:[[0, 0], [1, 2], [4, 5]]
0 and 1 wouldn't appear in pairs. So the numbers in result must be an integer.
What I have tried:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
output = [[]]
for pre, next_ in zip(hash_table, hash_table[1:]):
output[-1].append(pre)
if {next_, pre} == {0, 1}:
output.append([])
output[-1].append(hash_table[-1])
# the output is [[1], [0], [1, 1, 1], [0, 0, 0], [1, 1, 1]]
start = index = 0
result = []
while index < len(output):
# output[index]
if output[0] != 0:
res.append([start, math.ceil(len(output[index]))])
# I don't know how to handle the list "output".
# I couldn't know it. My mind has gone blank
start += len(output[index])/2
Any good ideas? I thought I made it too complicated.
You can use itertools.groupby to group the 0s and 1s:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
result = []
cur_ind = 0
for (val, vals) in itertools.groupby(hash_table):
vals = list(vals) # itertools doesn't make it a list by default
old_ind = cur_ind
cur_ind += len(vals)
if val == 0:
continue
result.append([old_ind // 2, (cur_ind - 1) // 2])
print(result)
Essentially, itertools.groupby will give an iterator of [(1, [1]), (0, [0]), (1, [1, 1, 1]), (0, [0, 0, 0]), (1, [1, 1, 1])] (more or less). We can iterate through this iterator and keep track if the index we're on by adding the length of the sublist to the current index. If the value is 1, then we have a run of ones so we append it to the results. The old_ind // 2 is integer division and is equivalent to int(old_ind / 2).
You could use groupby from itertools library:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
s = "".join(map(str, hash_table)) # s = "10111000111"
gs = [(i, list(g)) for i, g in itertools.groupby(s)]
idx, result = 0, []
for i, g in gs: # i can be '1' or '0' (i.e, if the group consist in 1's or 0's)
if i == '1':
result.append([idx/2, (idx + len(g) - 1)/2])
idx += len(g)
return result
I have a matrix (formed of a list of lists) that would look something like:
matrix = [[0, 0, 0, 0, 5],
[0, 0, 0, 4, 0],
[2, 0, 3, 0, 0],
[3, 2, 0, 2, 0],
[1, 0, 2, 0, 1]]
What I am struggling to create is a function that will take this matrix as an input, along with a position in the matrix - represented by a tuple - and return the two diagonals that intersect that point (without using NumPy). For example,
def getDiagonal(matrix, pos)
(row, col) = pos
# Smart diagonal finder code #
return (diag1, diag2)
diagonals = getDiagonals(matrix, (1, 1))
print(diagnonal[0])
print(diagnonal[1])
print(' ')
diagonals = getDiagonals(matrix, (1, 3))
print(diagnonal[0])
print(diagnonal[1])
Expected output:
OUT: [5, 4, 3, 2, 1]
OUT: [2, 2, 2]
OUT:
OUT: [0, 2, 2]
OUT: [0, 0, 3, 2, 1]
It is worth pointing out that I don't mind from which direction (bottom-to-top or top-to-bottom) the returned elements of the diagonals are. They could easily be done one way and revered using reverse() if need be.
I have looked at similar questions such as this one but this mainly deals with acquring the leading diagonals of a matrix and provides less information on getting the diagonals about a point.
Many thanks for your help and comments in advance!
A bit confusing, but I think this does it:
def getDiagonals(matrix, pos):
row, col = pos
nrows = len(matrix)
ncols = len(matrix[0]) if nrows > 0 else 0
# First diagonal
d1_i, d1_j = nrows - 1 - max(row - col, 0), max(col - row, 0)
d1_len = min(d1_i + 1, ncols - d1_j)
diag1 = [matrix[d1_i - k][d1_j + k] for k in range(d1_len)]
# Second diagonal
t = min(row, ncols - col - 1)
d2_i, d2_j = nrows - 1 - row + t, col + t
d2_len = min(d2_i, d2_j) + 1
diag2 = [matrix[d2_i - k][d2_j - k] for k in range(d2_len)]
return (diag1, diag2)
# Test
matrix = [[0, 0, 0, 0, 5],
[0, 0, 0, 4, 0],
[2, 0, 3, 0, 0],
[3, 2, 0, 2, 0],
[1, 0, 2, 0, 1]]
diagonals = getDiagonals(matrix, (1, 1))
print(diagonals[0])
# [1, 2, 3, 4, 5]
print(diagonals[1])
# [2, 2, 2]
diagonals = getDiagonals(matrix, (1, 3))
print(diagonals[0])
# [2, 2, 0]
print(diagonals[1])
# [1, 2, 3, 0, 0]
diagonals = getDiagonals(matrix, (2, 2))
print(diagonals[0])
# [1, 2, 3, 4, 5]
print(diagonals[1])
# [1, 2, 3, 0, 0]
So in a binary array I'm trying to find the points where a 0 and a 1 are next to each other, and redraw the array with these crossover points indicated by modifying the 0 value. Just wondering if there's a better way of comparing each of the values in a numpy array to the 8 surrounding values than using nested for loops.
Currently I have this, which compares to 4 surrounding just for readability here
for x in range(1, rows - 1):
for y in range(1, columns - 1):
if f2[x, y] == 0:
if f2[x-1, y] == 1 or f2[x+1, y] == 1 or f2[x, y-1] == 1 or f2[x, y+1] == 1:
f2[x, y] = 2
EDIT
For example
[[1, 1, 1, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1]]
to
[[1, 1, 1, 1, 1, 1, 1],
[1, 1, 2, 2, 2, 1, 1],
[1, 1, 2, 0, 2, 1, 1],
[1, 1, 2, 2, 2, 1, 1],
[1, 1, 1, 1, 1, 1, 1]]
This problem can be solved quickly with binary morphology functions
import numpy as np
from scipy.ndimage.morphology import binary_dilation, generate_binary_structure
# Example array
f2 = np.zeros((5,5), dtype=float)
f2[2,2] = 1.
# This line determines the connectivity (all 8 neighbors or just 4)
struct_8_neighbors = generate_binary_structure(2, 2)
# Replace cell with maximum of neighbors (True if any neighbor != 0)
has_neighbor = binary_dilation(f2 != 0, structure=struct_8_neighbors)
# Was cell zero to begin with
was_zero = f2 == 0
# Update step
f2[has_neighbor & was_zero] = 2.
Say i have two arrays:
a = [1, 1, 1] and
b = [0, 0]
I want to concatenate these randomly in a seperate variable c, until i reach some desired length of c. So something like:
N = 10
c = random_concatenating((a, b), N)
[1, 1, 1, 0, 0, 1, 1, 1, 0, 0]
A verbose version:
import random
a = [1, 1, 1]
b = [0, 0]
N = 10
def random_concatenating(iterable, n):
rv = []
l = [*iterable]
print(l)
while len(rv) < n:
rv.extend(random.choice(l))
return rv[:n]
c = random_concatenating((a, b), N)
print(c)
Prints (for example):
[1, 1, 1, 0, 0, 1, 1, 1, 1, 1]
You can use itertools.cycle to repeat the lists as many times as you need then slice them to the needed list
>>> from itertools import cycle, islice
>>> a = [1, 1, 1]
>>> b = [0, 0]
>>> N = 10
>>> list(islice(cycle(a+b), N))
[1, 1, 1, 0, 0, 1, 1, 1, 0, 0]
This is one approach. Using random.choice
import random
a = [1, 1, 1]
b = [0, 0]
N = 10
foo = a + b
c = []
for i in range(N):
c.append(random.choice(foo))
print(c)
Output:
[0, 1, 1, 1, 1, 0, 1, 1, 1, 1]
One-line
c = [random.choice(foo) for i in range(N)]