Plotting static Game of Life patterns in Python Matplotlib - python

There are a number of Python programs around for playing the classical 'Game of Life', and most of them offer nice graphic animations which allow one to follow the fantastic patterns emerging from the game. Such programs are fine for interactive use on a computer, but I have found very difficult to find one that provides static graphic output intended for printed pages (like those displayed in the 1970 article by Martin Gardner which presented the Game of Life to the world: The fantastic combinations of John Conway's new solitaire game "life"). There are many programs that offer text-based output of Life patterns, but I have not found any one capable of doing the same thing with Matplolib graphics. So, I started writing one, as shown in the code below:
import matplotlib.pyplot as plt
def iterate(Z):
shape = len(Z), len(Z[0])
N = [[0,]*(shape[0]+2) for i in range(shape[1]+2)]
# Compute number of neighbours for each cell
for x in range(1,shape[0]-1):
for y in range(1,shape[1]-1):
N[x][y] = Z[x-1][y-1]+Z[x][y-1]+Z[x+1][y-1] \
+ Z[x-1][y] +Z[x+1][y] \
+ Z[x-1][y+1]+Z[x][y+1]+Z[x+1][y+1]
# Update cells
for x in range(1,shape[0]-1):
for y in range(1,shape[1]-1):
if Z[x][y] == 0 and N[x][y] == 3:
Z[x][y] = 1
elif Z[x][y] == 1 and not N[x][y] in [2,3]:
Z[x][y] = 0
return Z
# The 'beehive' pattern
Z = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
n_generations = 10
fig, axes = plt.subplots(2, 5, figsize=(8, 8))
for i in range(n_generations):
iterate(Z)
ax = axes.flat[i]
ax.imshow(Z, interpolation='nearest', cmap=plt.cm.binary)
ax.set_axis_off()
ax.set_title('Generation {}'.format(i+1))
plt.grid(True)
plt.tight_layout()
plt.show()
This works, but it far from good, because:
(1) I would like that each plot showed grid lines, so that they could reproduce the original figures of Gardner's article, but I have not been able to found out how to do that;
(2) I would also like to be able to use spheres instead of squares for representing the living cells (just like they appear in Gardner's article);
Any help towards improving this code will be much appreciated!

To produce grid lines you need ticks at the respective positions. Using a MultipleLocator would produce ticks at multiples of 1.
Circles are standard scatter markers. You can plot the data as scatter instead of image.
Together it might look as follows, where I also made the code a bit more compact.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
def iterate(Z):
# http://www.labri.fr/perso/nrougier/from-python-to-numpy/code/game_of_life_numpy.py
N = (Z[0:-2, 0:-2] + Z[0:-2, 1:-1] + Z[0:-2, 2:] +
Z[1:-1, 0:-2] + Z[1:-1, 2:] +
Z[2: , 0:-2] + Z[2: , 1:-1] + Z[2: , 2:])
birth = (N == 3) & (Z[1:-1, 1:-1] == 0)
survive = ((N == 2) | (N == 3)) & (Z[1:-1, 1:-1] == 1)
Z[...] = 0
Z[1:-1, 1:-1][birth | survive] = 1
return Z
# The 'beehive' pattern
Z = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Z = np.array(Z)
X, Y = np.meshgrid(np.arange(Z.shape[1])+.5, np.arange(Z.shape[0])+.5)
fig, axes = plt.subplots(2, 5, figsize=(8, 4))
for i, ax in enumerate(axes.flat):
Z = iterate(Z)
ax.scatter(X[Z > 0], Y[Z > 0], color="k")
ax.grid(True, color="k")
ax.xaxis.set_major_locator(mticker.MultipleLocator())
ax.yaxis.set_major_locator(mticker.MultipleLocator())
ax.tick_params(size=0, length=0, labelleft=False, labelbottom=False)
ax.set(xlim=(0, Z.shape[1]), ylim=(Z.shape[0], 0),
title='Generation {}'.format(i+1), aspect="equal")
plt.tight_layout()
plt.show()

For the first problem, it's because you set axes off. I turned it on and make some tweaks:
n_generations = 10
fig, axes = plt.subplots(2, 5, figsize=(16, 8))
for i in range(n_generations):
iterate(Z)
ax = axes.flat[i]
ax.imshow(Z, interpolation='nearest', cmap=plt.cm.binary)
# ax.set_axis_off()
ax.set_xticks(np.arange(10)+.5)
ax.set_yticks(np.arange(10)+.5)
ax.set_xticklabels('')
ax.set_yticklabels('')
ax.set_title('Generation {}'.format(i+1))
plt.tight_layout()
plt.show()
Output:
For the other question. I don't think it's possible with imshow. You may need to write a custom function my_plot(Z, ax).

Related

Plot csv file taking the first column as x axis and the others as y axis once more

Can you please elaborate more how to use the solution of here once more? I have the same problem, I want to use the first column as the x axis and the following columns as y axis values. My code currently looks like this.
But basically I want to have it look like a scatter plot, with the values on each x value.
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_csv(
"ORBGRAND_Hamming_4LWmax_63storeLWsuccess", sep=", ")
[plt.plot(data[0], data[x]) for x in range(1, len(filecsv[:, 0]))]
plt.grid(True)
plt.suptitle("(63,45) BCH", fontsize=40)
plt.grid(True)
plt.yscale('log')
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.legend(loc='lower left')
plt.xlabel('$E_b/N_0$ (dB)', fontsize=20)
plt.ylabel('BLER', fontsize=20)
plt.legend(loc='lower left', prop={'size': 17})
plt.show()
My file looks like this:
0.000000, 0, 2, 0, 0, 1, 0, 0, 3, 0, 1
0.500000, 1, 0, 3, 3, 1, 0, 0, 3, 2, 1
1.000000, 4, 5, 0, 0, 0, 0, 0, 0, 0, 1
1.500000, 0, 0, 1, 3, 1, 0, 2, 0, 0, 0
2.000000, 1, 0, 0, 1, 0, 3, 0, 0, 0, 0
2.500000, 0, 0, 0, 0, 5, 1, 0, 1, 0, 1
3.000000, 3, 0, 1, 2, 0, 0, 0, 0, 1, 0
3.500000, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0
4.000000, 2, 2, 0, 0, 0, 0, 3, 0, 0, 0
4.500000, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0
5.000000, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0
5.500000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0
6.000000, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0
I get the following error:
Expected 85 fields in line 3, saw 88. Error could possibly be due to quotes being ignored when a multi-char delimiter is used.
This creates a scatter plot with the first column as x against all the following columns.
As commented above, your error is not from the plotting itself.
import pandas as pd
import matplotlib.pyplot as plt
# This is just a subset of your posted data
data = [[0.000000, 0, 2, 0, 0, 1, 0, 0, 3, 0, 1],
[0.500000, 1, 0, 3, 3, 1, 0, 0, 3, 2, 1],
[1.000000, 4, 5, 0, 0, 0, 0, 0, 0, 0, 1],
[1.500000, 0, 0, 1, 3, 1, 0, 2, 0, 0, 0],
[2.000000, 1, 0, 0, 1, 0, 3, 0, 0, 0, 0]]
# build the dataframe
column_names = [f'y{i}' for i in range(1, len(data[0]))]
column_names[0] = "x"
df = pd.DataFrame(data, columns=["x"] + column_names)
# You didn't specify what plot, I assumed you want all in one figure?
# This plots all the values (y) to the same x colum
plt.figure()
for y in column_names:
plt.scatter(df["x"], df[y], label=y)
plt.legend()
# Add your aesthetics here

Randomizing list of lists without overlap

I'm making a function that randomizes a gameboard (represented by a list of ten lists of ten) for the game battleship. what my function does currently is randomly place numbers on the board representing ships. My function also makes sure that the ships don't loop around the edge of the board and appear on the other side, as well as randomly generating the orientation of the ships. what my function fails to achieve however is making sure that the "ships" don't overlap onto each other. I've been having trouble coming up with a solution, though I'm sure its a pretty simple one. is there a way to achieve my goal?
import random
l = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
for a in range(1, 5):
p = random.randrange(0, 10, 1)
o = random.randrange(0, 10, 1)
#the p and o variables determine the coordinates of the starting point
r = random.randrange(1, 3)
#the r variable randomizes orientation of the ship
if r == 1:
for n in range(1, 7 - a):
#the function uses the length of the ship to determine whether or not
#the ship will go off the end of the board
if o < 6 - a:
l[p][(6 - a) - n] = 6 - a
else:
l[p][o-n] = 6 - a
else:
for e in range(1, 7 - a):
if p < 6-a:
l[(6-a) - e][o] = 6-a
else:
l[p - e][o] = 6-a
for v in range(0, len(l)):
print(l[v])
Output example:
[0, 3, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 3, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 3, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 0, 0, 5, 5, 5, 5, 5, 0]
[0, 4, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 4, 0, 0, 0, 0, 0, 0, 2, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 2, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Output with overlap (the five ship is covered by the three ship):
[0, 0, 0, 0, 5, 5, 5, 5, 3, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 3, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 3, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[2, 2, 0, 0, 0, 0, 0, 0, 0, 0]
[4, 4, 4, 4, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
At the risk of over-complicating things, I suggest an object oriented approach. It is possible to modify your method, but I find it gets messy fast.
In the place function, we assemble a list of locations to place in order to make the ship. We then check if there is any ship already located there grid[y][x] != 0. If so, we need to re-generate random values for the position and rotation, then we can try to place again.
import random
GRID_WIDTH, GRID_HEIGHT = 10, 10 # constants representing width and height of the board
grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)] # generate a 10x10 grid of 0's
class Ship:
def __init__(self, length):
self.length = length
self.x, self.y, self.horizontal = None, None, None
self.generate()
def generate(self): # randomize position and rotation
self.x = random.randint(0, GRID_WIDTH-self.length)
self.y = random.randint(0, GRID_HEIGHT-self.length)
self.horizontal = random.choice([True, False])
self.place()
def place(self): # place ship on the grid
locations = []
if self.horizontal:
for x in range(self.x, self.x+self.length):
locations.append((x, self.y))
else: # if vertical
for y in range(self.y, self.y+self.length):
locations.append((self.x, y))
for x, y in locations:
if grid[y][x] != 0: # if occupied, regenerate whole ship
self.generate()
return
for x, y in locations: # actually place ship now
grid[y][x] = self.length
ships = []
for ship_length in range(2, 6):
ships.append(Ship(ship_length))
for row in grid: # print the board
print(row)
# for row in grid: # print the board without 0's
# print(str(row).replace('0', ' '))
Let me know if you have any questions about the code.

bounding box from 2d numpy array [duplicate]

This question already has answers here:
How to find top_left, top_right, bottom_left, right coordinates in 2d mask where cell has specified value?
(4 answers)
Closed 3 years ago.
I am trying to generate a bounding box for object detection in an image. I read the image and generate a binary 2d numpy array such as:
array([[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]])
The 1 represent pixels that are to be within the bounding box in the image. How can I get the x,y coordinates of the top left point, and then the length of x,y?
Check this simple code:
import numpy as np
a = np.array(
[[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]])
x,y = np.where(a)
top_left = x.min(), y.min()
bottom_right = x.max(), y.max()

Python matlibplot: visualizing a matrix of integers (-1, 0 or 1) with circles (same radius, different color)

I have a square matrix filled by -1, 0 or 1. I would like to visualize this matrix with spheres or circles of the same radius. Radius, indeed is not important at all. Those circles though, must have a different colour according to the number of the matrix cell.
For example:
10 x 10 matrix -> 100 circles on a plane, 10 rows x 10 columns
Color of circle in position (2,9) depending on number of matrix in position (2,9).
Thank you!
People I know told me to use matlibplot, but I am new to Python and
I have many issues!
This is what I did up to now:
{`
import numpy as np
#from implementations import *
#from config import *
import matplotlib.pyplot as plt
A = np.array([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 2, -1, -1,-1, 2, 1, 0, 0],
[0, 1, 2, -1, -1,-1, 2, 1, 0, 0],
[0, 1, 2, -1, -1,-1, 2, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
rows=len(A) # finding number rows of lattice, so no need to worry about it!
columns=len(A[0]) # finding number columns of lattice, so no need to worry about it!
fig, ax = plt.subplots()
for i in range(rows):
for j in range(columns):
if A[i][j]==-1:
circle1 = plt.Circle((i*4, j*4), 2, color='blue')
fig = plt.gcf()
ax = fig.gca()
ax.add_artist(circle1)
if A[i][j]== 1:
circle2 = plt.Circle((i*4, j*4), 2, color='yellow')
fig = plt.gcf()
ax = fig.gca()
ax.add_artist(circle2)
`}
Here is the matplotlib code that uses scatter matrix:
# Imports
import matplotlib.pyplot as plt
from itertools import chain
# Create plot
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Here is our matrix. Note, that it is expected to be rectangular!
matrix = [
[0, 1, 0,1],
[-1,1, 0,0],
[1,-1,-1,1],
]
# Get X-length
X = len(matrix[0])
# Get Y-length
Y = len(matrix)
# Construct grids for scatter
x_grid = list(range(X)) * Y # 1,2,3,4,1,2,3,4...
y_grid = [y for y in range(Y) for _ in range(X)] # 1,1,1,1,2,2,2,2...
# Flatten the matrix because ax.scatter uses flat arrays
matrix_grid = list(chain(*matrix))
plt.scatter(
x_grid, # X-grid array of coordinates
y_grid, # Y-grid array of coordinates
c=matrix_grid, # Our flatten matrix of -1/0/1s
cmap='gist_rainbow' # Color map - defines colors
)
You can directly use a scatter as follows:
import numpy as np
import matplotlib.pyplot as plt
A = np.array([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 2, -1, -1,-1, 2, 1, 0, 0],
[0, 1, 2, -1, -1,-1, 2, 1, 0, 0],
[0, 1, 2, -1, -1,-1, 2, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
])
X,Y = np.meshgrid(np.arange(A.shape[1]), np.arange(A.shape[0]))
plt.scatter(X.flatten(), Y.flatten(), c=A.flatten())
plt.show()

matplotlib plot in a loop

How to connect every 10th point?
import numpy as np
import matplotlib.pyplot as plt
if __name__ == '__main__':
#points = np.fromfile('test_acc_history.txt')
points = np.array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0])
plt.figure(100)
plt.plot(points)
plt.show()
The output results in:
BUT, I wanna have a result which should look like a curve:
In order to plot every nth point, you can slice the array, points[::n]. To then make sure to have them plotted at the correct position, you also need to supply a list of x values, which is every nth point from the integer number range.
import numpy as np
import matplotlib.pyplot as plt
points = np.array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.7, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0])
plt.plot(points)
plt.plot(range(len(points))[::10],points[::10] )
plt.show()

Categories