I have a file containing 0 and 1s here: https://easyupload.io/wvoryj.
How can I fill the shape of these structures with 1s? I think binary_fill_holes is not working because the outline is not continuous.
plot showing structures
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage
mask = np.loadtxt('mask.txt', dtype=int)
mask = ndimage.binary_fill_holes(mask).astype(int)
fig, ax = plt.subplots()
plt.imshow(mask)
plt.show()
This would be my approach:
First fill up the gaps with a 2D convolution
run a cumsum over all rows to fill the outlines
divide by the last (or highest) number of the row
set everything larger than 1 back to 1
I hope that helped
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import convolve2d
mask = np.loadtxt('mask.txt', dtype=int)
# run a convolution over the mask to fill out the empty spaces
conv_mat = np.array(3*[[0,1,0]])
mask_continuous = convolve2d(mask, conv_mat)
# add up all numbers from left to right...
# ...and divide by the last value of the row
mask_filled = np.array([np.cumsum(i) / np.cumsum(i)[-1] for i in mask_continuous])
# reset everything larger than 1 to 1
mask_filled[mask_filled>1] = 1
fig, ax = plt.subplots()
plt.imshow(mask_filled)
plt.show()
Related
I have a three columns data rP.csv, file. it is sparse in x-direction and dense in y-direction, and I want to plot contour plot using these x-y-z data but I got nothing, only scatters with its color represent z value in the picture
ideal result: if I can connect equal values and then fill the between will be excellent!
Below is my code. I am confused that I already have a X,Y meshgrid and Z pivot_table values, why can't I contour plot.
And is there any other way to connect a series of equal values if I can't contour plot.
PS:When interpolate datas between "x-slices" the value is not reasonable, so I quit using griddata to interpolate.
import os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import MultipleLocator
import pandas as pd
from matplotlib import colors
from matplotlib.colors import *
from scipy.interpolate import griddata
fig, ax = plt.subplots( )
cmap = plt.get_cmap("Paired", 11)
norm= BoundaryNorm(np.arange(0,1.1,0.1),cmap.N)
# data -----------------
rP=pd.read_csv('rP.csv')
x,y,z=rP['O'],rP['T'],rP['s']
X_unique = np.sort(x.unique())
Y_unique = np.sort(y.unique())
X, Y = np.meshgrid(X_unique,Y_unique)
Z = rP.pivot_table(index='O', columns='T', values='s').T.values
# -----------------
# scatter ---------------
pplot=ax.scatter(x,y, marker='s',s=50,c=z,cmap=cmap,norm=norm)
# ---------------
# contour---- I want to draw contours but nothing happend, I think the data format of X,Y,Z is correct------------
pplot2=ax.contour(X,Y,Z,2,linewidths=5,colors='k')
plt.clabel(pplot2,fontsize=10)
# ---------------
# contour lines ---------- I collected contour lines, and lines is blank
lines = []
for line in pplot2.collections[0].get_paths():
lines.append(line.vertices)
# ---------------
ax.set_xlim(0,1)
plt.colorbar(pplot,ax=ax,ticks=np.arange(0,1.1,0.1),format='%.1f',shrink=1)
Thank you for reading here, I hope you have a wonderful day!
I have a gas map in type of numpy array that has value of (-1,unexplored area),(100,obstacle/wall) and (range of 20.0 until 30.0, gas concentration reading) like enter image description here
but when I display it using matplotlib i cannot specific the different color for wall(black), unexplored area(grey), and gas concentration (red contour)
the figure display by the matplotlib is enter image description here
this is the program that i use to display in matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
df = pd.read_excel("/content/drive/MyDrive/Japan Data/291,194/a 291,194 (11).xlsx")
array = df.to_numpy()
x =range(0,400)
y = range(0,399)
plt.contourf(x,y,array,cmap="Reds_r")
plt.colorbar(label= "Gas concentration")
`
how i can display the gas map using matplotlib as the first figure?
Sorry that my comment sounded simplistic. I had more trouble following it that I should have, and I apologize.
Anyway, I was having problems getting the colormaps to accept the discrete values -1 and 100. I could get it to map between 20 and 30 well, but not those edge cases. I ended up doing a brute-force method that worked, but is much less elegant. Hopefully someone more knowledgeable might provide a better way. What I did was translate your 2D array of values into a 3D array of RGBA values, then plot it with imshow. Here's the code:
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import pandas as pd
import numpy as np
df = pd.read_excel('./kernel.xlsx', header=None)
array = df.to_numpy()
fig, ax = plt.subplots()
x = range(0, 400)
y = range(0, 400)
gas_data = array[(array > -1) & (array < 100)]
gas_min = gas_data.min()
gas_max = gas_data.max()
rows, cols = array.shape
arr3d = np.zeros((rows, cols, 4))
norm = Normalize(vmin=gas_min, vmax=gas_max)
for row_i in range(rows):
for col_j in range(cols):
val = array[row_i, col_j]
if val == -1: # Unexplored
color = (1, 1, 1, 1) # white
elif val == 100: # Wall
color = (0, 0, 0, 1) # black
else:
color = plt.cm.Reds_r(norm(val))
arr3d[row_i, col_j, :] = color
# print(row_i+1, " of ", rows)
ax.imshow(np.flipud(arr3d))
Resulting plot:
I have 2 tables a 10 by 110 and a 35 by 110 and both contain random numbers from a exponential distribution function provided by my professor. The assignment is to prove the central limit theorem in statistics.
What I thought to try is:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
"importing data"
df1 = pd.read_excel(r'C:\Users\Henry\Desktop\n10.xlsx')
df2 = pd.read_excel(r'C:\Users\Henry\Desktop\n30.xlsx')
df1avg = pd.read_excel(r'C:\Users\Henry\Desktop\n10avg.xlsx')
df2avg = pd.read_excel(r'C:\Users\Henry\Desktop\n30avg.xlsx')
"plotting n10 histogram"
plt.hist(df1, bins=34)
plt.hist(df1avg, bins=11)
"plotting n30 histogram"
plt.hist(df2, bins=63)
plt.hist(df2avg, bins=11)
Is that ok or do I need to format the tables into a singular column, and if so what is the most efficient way to do that?
I suspect that you will want to flatten your dataframe first, as illustrated below.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
N = np.random.exponential(1, [40, 5])
df = pd.DataFrame(N) # convert to dataframe
bin_edges = np.linspace(0,6,30)
plt.figure()
plt.hist(df, bins = bin_edges, density = True)
plt.xlabel('Value')
plt.ylabel('Probability density')
The multiple (5) colours of lines per bin shows the histograms for each column of the data frame.
Fortunately, this is not hard to adjust. You can convert the data frame to a numpy array and flatten it:
plt.hist(df.to_numpy().flatten(), bins = bin_edges, density = True)
plt.ylabel('Probability density')
plt.xlabel('Value')
I have a small script that creates a matplotlib graph with 2000 random points following a random walk.
I'm wondering if there is a simple way to change the number of points on the y-axis as well as how I can extract these values?
When I run the code below, I get 5 points on the Y-axis but I'm looking for a way to expand this to 20 points as well as creating an array or series with these values. Many thanks in advance.
import matplotlib.pyplot as plt
dims = 1
step_n = 2000
step_set = [-1, 0, 1]
origin = np.zeros((1,dims))
random.seed(30)
step_shape = (step_n,dims)
steps = np.random.choice(a=step_set, size=step_shape)
path = np.concatenate([origin, steps]).cumsum(0)
plt.plot(path)
import matplotlib.pyplot as plt
import numpy as np
import random
dims = 1
step_n = 2000
step_set = [-1, 0, 1]
origin = np.zeros((1,dims))
random.seed(30)
step_shape = (step_n,dims)
steps = np.random.choice(a=step_set, size=step_shape)
path = np.concatenate([origin, steps]).cumsum(0)
#first variant
plt.plot(path)
plt.locator_params(axis='x', nbins=20)
plt.locator_params(axis='y', nbins=20)
You can use locator_params in order to specify the number of ticks. Of course you can retrieve these points. For this you must create a subplot with ax, and then you can get the y_ticks with get_yticks.
#second variant
# create subplot
fig, ax = plt.subplots(1,1, figsize=(20, 11))
img = ax.plot(path)
plt.locator_params(axis='y', nbins=20)
y_values = ax.get_yticks() # y_values is a numpy array with your y values
I am trying to rebuild an image that I previously decomposed with SVD. The image is this:
I successfully decomposed the image with this code:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
s an array of the singular values of the image. The more singular values I take, the more the reconstructed image is similar to the original one.
For example, if I take 20 singular values:
n = 20
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
plt.show()
I would like to fix the minumum number of singular values in order to get a good result: an image pretty similary to the original one. Moreover, I would like to see how much the result changes when I take a higher number of singular values. I tried with an animation without success:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig = plt.figure()
def update(i):
S = np.zeros(np.shape(img))
n = 20
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
ani = FuncAnimation(fig = fig, func = update, frames = 20, interval = 10)
plt.show()
If you plot the s singular values you can see a very steep decreasing curve, better if you use a log scale for the y axis:
plt.semilogy(s, 'k-')
As you can see, the first 50 singular values are the most important ones: almost everyone more that 1000. Values from the ~50th to the ~250th are an order of magnitude lower and their values decreases slowly: the slope of the curve is contained (remember the logarithmic y scale). That beeing said I would take the first 50 elements to rebulid your image.
Regarding the animation:
while the animation updates frame by frame, the counter i is increased by 1. In your code, you mistakenly use i to slice the s and define S; you should rename the counter.
Moreover, as animation goes on, you need to take an increasing number of singular values, this is set by n which you keep constant frame by frame. You need to update n at each loop, so you can use it as the counter.
Furthermore, you need the erase the previous plotted image, so you need to add a plt.gca().cla() at the beginning of the update function.
Check the code below for reference:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig, ax = plt.subplots(1, 2, figsize = (4, 4))
ax[0].imshow(img)
ax[0].axis('off')
ax[0].set_title('Original')
def init():
ax[1].cla()
ax[1].imshow(np.zeros(np.shape(img)))
ax[1].axis('off')
ax[1].set_title('Reconstructed\nn = 00')
def update(n):
ax[1].cla()
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
ax[1].imshow(recon_img)
ax[1].axis('off')
ax[1].set_title(f'Reconstructed\nn = {n:02}')
ani = FuncAnimation(fig = fig, func = update, frames = 50, init_func = init, interval = 10)
ani.save('ani.gif', writer = 'imagemagick')
plt.show()
which gives this animation:
As you can see, the first 50 elements are enough to rebuild you image pretty well. The rest of the elements adds some noise and changes a little the background.