I want to save a plot (of a polygon that builds via code) to a NumPy (not as an image file), but I don't know how can do it?!
this is my code:
fig, ax = plt.subplots(figsize=(5.12, 5.12))
N = 3
val = np.random.rand(N, 2, 3)
patches = []
for i in range(3):
patches.append(Polygon(val[:, :, i], True))
p = PatchCollection(patches, alpha=0.6)
p.set_array(np.array([500,23,1002])) # assign values
ax.add_collection(p)
fig.colorbar(p)
plt.axis('off')
plt.show()
and plot of it:
a plot after run above code
can anyone tell me, how can I do it?
If you want to save the polygons, you only need to remember the vertices of each polygon. For each shape, and set of vertices, it may be better to save as a dictionary, because these are characteristics of the object which .json files are meant to handle.
From the docs, Polygon() has a method that retrieves the vertices for that polygon .get_xy() so we can use this to get a tuple of coordinates for each vertex.
The next thing you specified is to be able to save the values assigned within each triangle given by the array that is plugged into ax.add_collection. We can move this to the top of the code, and then iterate through the list of values, adding them as values in a dictionary of keys.
Then the code becomes:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
import json # To save the dictionary format
fig, ax = plt.subplots(figsize=(5.12, 5.12))
N = 3
val = np.random.rand(N, 2, 3)
fill = [500,23,1002] # The fill values you want to save
patches = [] # The list to save each triangle object
keys = 'tri' # The names of each triangle
tri_dict = {} # Initialize the final dictionary
for i in range(3):
poly = Polygon(val[:, :, i], True)
vert = poly.get_xy() # Get the vertices for the Polygon
patches.append(poly)
my_dict = { # Create a dictionary of chartacteristics for a single triangle
'vertices':vert.tolist(),
'fill': fill[i]
}
tri_dict[keys+f'{i}'] = my_dict # For the tiangle name as the key,
# assign the corresponding values
# print(tri_dict) # To show that you are saving the information of each triangle within
# a dictionary of dictionaries
# Saving the dictionary as .json
jsonString = json.dumps(tri_dict, indent=4)
jsonFile = open(r"<YOUR PATH HERE>\\"+"data.json", "w")
jsonFile.write(jsonString)
jsonFile.close()
# Plot the resulting triangles
p = PatchCollection(patches, alpha=0.6)
p.set_array(fill)
ax.add_collection(p)
fig.colorbar(p)
plt.axis('off')
plt.show()
From here, you should be able to play with the structure to fit your use-case, but for the question asked, this should suffice. Hope this helps!
Related
For a ML project I'm currently on, I need to verify if the trained data are good or not.
Let's say that I'm "splitting" the sky into several altitude grids (let's take 3 values for the moment) and for a given region (let's say, Europe).
One grid could be a signal reception strength (RSSI), another one the signal quality (RSRQ)
Each cell of the grid is therefor a rectangle and it has a mean value of each measurement (i.e. RSSI or RSRQ) performed in that area.
I have hundreds of millions of data
In the code below, I know how to draw a coloured mesh with xarray for each altitude: I just use xr.plot.pcolormesh(lat,lon, the_data_set); that's fine
But this will only give me a "flat" figure like this:
RSSI value at 3 different altitudes
I need to draw all the pcolormesh() of a dataset for each altitude in such way that:
1: I can have the map at the bottom
2: Each pcolormesh() is stacked and "displayed" at its altitude
3: I need to add a 3d scatter plot for testing my trained data
4: Need to be interactive as I have to zoom in areas
For 2 and 3 above, I managed to do something using plt and cartopy :
enter image description here
But plt/cartopy combination is not as interactive as plotly.
But plotly doesn't have the pcolormesh functionality
And still ... I don't know in anycase, how to "stack" the pcolormesh results that I did get above.
I've been digging Internet for few days but I didn't find something that could satisfy all my criteria.
What I did to get my pcolormesh:
import numpy as np
import xarray as xr
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
class super_data():
def __init__(self, lon_bound,lat_bound,alt_bound,x_points,y_points,z_points):
self.lon_bound = lon_bound
self.lat_bound = lat_bound
self.alt_bound = alt_bound
self.x_points = x_points
self.y_points = y_points
self.z_points = z_points
self.lon, self.lat, self.alt = np.meshgrid(np.linspace(self.lon_bound[0], self.lon_bound[1], self.x_points),
np.linspace(self.lat_bound[0], self.lat_bound[1], self.y_points),
np.linspace(self.alt_bound[0], self.alt_bound[1], self.z_points))
self.this_xr = xr.Dataset(
coords={'lat': (('latitude', 'longitude','altitude'), self.lat),
'lon': (('latitude', 'longitude','altitude'), self.lon),
'alt': (('latitude', 'longitude','altitude'), self.alt)})
def add_data_array(self,ds_name,ds_min,ds_max):
def create_temp_data(ds_min,ds_max):
data = np.random.randint(ds_min,ds_max,size=self.y_points * self.x_points)
return data
temp_data = []
# Create "z_points" number of layers in the z axis
for i in range(self.z_points):
temp_data.append(create_temp_data(ds_min,ds_max))
data = np.concatenate(temp_data)
data = data.reshape(self.z_points,self.x_points, self.y_points)
self.this_xr[ds_name] = (("altitude","longitude","latitude"),data)
def plot(self,dataset, extent=None, plot_center=False):
# I want t
if np.sqrt(self.z_points) == np.floor(np.sqrt(self.z_points)):
side_size = int(np.sqrt(self.z_points))
else:
side_size = int(np.floor(np.sqrt(self.z_points) + 1))
fig = plt.figure()
i_ax=1
for i in range(side_size):
for j in range(side_size):
if i_ax < self.z_points+1:
this_dataset = self.this_xr[dataset].sel(altitude=i_ax-1)
# Initialize figure with subplots
ax = fig.add_subplot(side_size, side_size, i_ax, projection=ccrs.PlateCarree())
i_ax += 1
ax.coastlines()
this_dataset.plot.pcolormesh('lon', 'lat', ax=ax, infer_intervals=True, alpha=0.5)
else:
break
plt.tight_layout()
plt.show()
if __name__ == "__main__":
# Wanted coverage :
lons = [-15, 30]
lats = [35, 65]
alts = [1000, 5000]
xarr = super_data(lons,lats,alts,10,8,3)
# Add some fake data
xarr.add_data_array("RSSI",-120,-60)
xarr.add_data_array("pressure",700,1013)
xarr.plot("RSSI",0)
Thanks for you help
I would like to let my code read my txt file one by one, convert it into image, and save it with different image, i.e 300s, 600s, 900s,....
I made the code down and it says only the path but did not proceed with its next code.
Could you give me some advice or find the missing or mistaken part of my codes?
import numpy as np
import matplotlib.pyplot as plt
import glob
import cv2
import os
path = './Master_thesis/Code/dnn_simulation_result/'
interval = 300
folders = []
#r=root, d=dirctories, f=files
for r, d, f in os.walk(path):
if not d:
folders.append(r)
for f in folders:
print(r)
def txt2image(folders, skiprows) :
for folder_name in folders:
IsFile=(glob.glob(folder_name+"/*.*"))
for file in IsFile:
myArray = np.loadtxt(path, skiprows = skiprows)
# Set the nodata values to nan
myArray[myArray == -9999] = np.nan
# PRISM data is stored as an integer but scaled by 100
myArray *= 1
# Plot PRISM array again
fig, ax = plt.subplots()
ax.set_title('Flood area')
# Get the img object in order to pass it to the colorbar function
img_plot = ax.imshow(myArray, cmap='jet')
# Place a colorbar next to the map
cbar = fig.colorbar(img_plot)
ax.grid(True)
plt.show()
txt2image = cv2.imwrite('D:/Master_thesis/Code/dnn_simulation_result/dnn_simulation_result/{}.jpg', img_plot)
return txt2image
txt2image(folders, 0)
I am trying to plot some data with a discrete color bar. I was following the example given (https://gist.github.com/jakevdp/91077b0cae40f8f8244a) but the issue is this example does not work 1-1 with different spacing. For example, the spacing in the example in the link is for only increasing by 1 but my data is increasing by 0.5. You can see the output from the code I have.. Any help with this would be appreciated. I know I am missing something key here but cant figure it out.
import matplotlib.pylab as plt
import numpy as np
def discrete_cmap(N, base_cmap=None):
"""Create an N-bin discrete colormap from the specified input map"""
# Note that if base_cmap is a string or None, you can simply do
# return plt.cm.get_cmap(base_cmap, N)
# The following works for string, None, or a colormap instance:
base = plt.cm.get_cmap(base_cmap)
color_list = base(np.linspace(0, 1, N))
cmap_name = base.name + str(N)
return base.from_list(cmap_name, color_list, N)
num=11
x = np.random.randn(40)
y = np.random.randn(40)
c = np.random.randint(num, size=40)
plt.figure(figsize=(10,7.5))
plt.scatter(x, y, c=c, s=50, cmap=discrete_cmap(num, 'jet'))
plt.colorbar(ticks=np.arange(0,5.5,0.5))
plt.clim(-0.5, num - 0.5)
plt.show()
Not sure what version of matplotlib/pyplot introduced this, but plt.get_cmap now supports an int argument specifying the number of colors you want to get, for discrete colormaps.
This automatically results in the colorbar being discrete.
By the way, pandas has an even better handling of the colorbar.
import numpy as np
from matplotlib import pyplot as plt
plt.style.use('ggplot')
# remove if not using Jupyter/IPython
%matplotlib inline
# choose number of clusters and number of points in each cluster
n_clusters = 5
n_samples = 20
# there are fancier ways to do this
clusters = np.array([k for k in range(n_clusters) for i in range(n_samples)])
# generate the coordinates of the center
# of each cluster by shuffling a range of values
clusters_x = np.arange(n_clusters)
clusters_y = np.arange(n_clusters)
np.random.shuffle(clusters_x)
np.random.shuffle(clusters_y)
# get dicts like cluster -> center coordinate
x_dict = dict(enumerate(clusters_x))
y_dict = dict(enumerate(clusters_y))
# get coordinates of cluster center for each point
x = np.array(list(x_dict[k] for k in clusters)).astype(float)
y = np.array(list(y_dict[k] for k in clusters)).astype(float)
# add noise
x += np.random.normal(scale=0.5, size=n_clusters*n_samples)
y += np.random.normal(scale=0.5, size=n_clusters*n_samples)
### Finally, plot
fig, ax = plt.subplots(figsize=(12,8))
# get discrete colormap
cmap = plt.get_cmap('viridis', n_clusters)
# scatter points
scatter = ax.scatter(x, y, c=clusters, cmap=cmap)
# scatter cluster centers
ax.scatter(clusters_x, clusters_y, c='red')
# add colorbar
cbar = plt.colorbar(scatter)
# set ticks locations (not very elegant, but it works):
# - shift by 0.5
# - scale so that the last value is at the center of the last color
tick_locs = (np.arange(n_clusters) + 0.5)*(n_clusters-1)/n_clusters
cbar.set_ticks(tick_locs)
# set tick labels (as before)
cbar.set_ticklabels(np.arange(n_clusters))
Ok so this is the hack I found for my own question. I am sure there is a better way to do this but this works for what I am doing. Feel free to suggest a better way to do this.
import numpy as np
import matplotlib.pylab as plt
def discrete_cmap(N, base_cmap=None):
"""Create an N-bin discrete colormap from the specified input map"""
# Note that if base_cmap is a string or None, you can simply do
# return plt.cm.get_cmap(base_cmap, N)
# The following works for string, None, or a colormap instance:
base = plt.cm.get_cmap(base_cmap)
color_list = base(np.linspace(0, 1, N))
cmap_name = base.name + str(N)
return base.from_list(cmap_name, color_list, N)
num=11
plt.figure(figsize=(10,7.5))
x = np.random.randn(40)
y = np.random.randn(40)
c = np.random.randint(num, size=40)
plt.scatter(x, y, c=c, s=50, cmap=discrete_cmap(num, 'jet'))
cbar=plt.colorbar(ticks=range(num))
plt.clim(-0.5, num - 0.5)
cbar.ax.set_yticklabels(np.arange(0.0,5.5,0.5))
plt.show()
For some reason I cannot upload the image associated with the code above. I get an error when uploading so not sure how to show the final example. But simply I set the color bar axes for tick labels for a vertical color bar and passed in the labels I want and it produced the correct output.
So I have been trying to load text files onto multiple subplots but the plots always seem to come up as one text file. Can anyone point me into the right #direction as to how to go about this?
import numpy as np
import matplotlib.pyplot as plt
RiverData1 = np.loadtxt('Gray1961.txt', skiprows = 2)
RiverData2 = np.loadtxt('Hack1957.txt', skiprows = 2)
RiverData3 = np.loadtxt('Rignon1996.txt', skiprows = 2)
RiverData4 = np.loadtxt('Robert1990.txt', skiprows = 2)
RiverData5 = np.loadtxt('Langbein1947_p145.txt', skiprows = 2)
RiverData6 = np.loadtxt('Langbein1947_p146.txt', skiprows = 2)
RiverData7 = np.loadtxt('Langbein1947_p149.txt', skiprows = 2)
RiverData8 = np.loadtxt('Langbein1947_p152.txt', skiprows = 2)
plotnums = 1
for plotnums in range (1,9):
plt.subplot(2,4,plotnums)
plt.plot((RiverData1[:,0]), (RiverData1[:,1]),'ko')
plt.plot((RiverData2[:,0]), (RiverData2[:,1]),'ko')
plt.plot((RiverData3[:,0]), (RiverData3[:,1]),'ko')
plt.plot((RiverData4[:,0]), (RiverData4[:,1]),'ko')
plt.plot((RiverData5[:,0]), (RiverData5[:,1]),'ko')
plt.plot((RiverData6[:,0]), (RiverData6[:,1]),'ko')
plt.plot((RiverData7[:,0]), (RiverData7[:,1]),'ko')
plt.plot((RiverData8[:,0]), (RiverData8[:,1]),'ko')
plt.xlabel('River Length (km)')
plt.ylabel('Area (Km$^2$)')
plt.xscale('log')
plt.yscale('log')
plotnums=plotnums+1
plt.show()
I suggest loading the data inside the loop as well. Additionally, you should capture the axis-handle in a variable to control which axis is used for plotting the data. To avoid any data artifacts, I suggest setting the variables to None at the end of each iteration.
import numpy as np
import matplotlib.pyplot as plt
# store your file names in a list to be able to iterate over them:
FILES = ['Gray1961.txt','Hack1957.txt','Rignon1996.txt',\
'Robert1990.txt','Langbein1947_p145.txt','Langbein1947_p146.txt',\
'Langbein1947_p149.txt','Langbein1947_p152.txt']
# specify desired conversion factors for each file, separated by x and y
xFactor =[1.00, 1.00, 1.00, 1.00\
2.59, 2.59, 2.59, 2.59]
yFactor = [1.000, 1.000, 1.000, 1.000\
1.609, 1.609, 1.609, 1.609]
# loop through all files;
# range(len(FILES)) returns a list of integers from 0 to 7 in this example
for n in range(len(FILES)):
# load the data from each file:
RiverData = np.loadtext(FILES[n], skiprows = 2)
# convert the data per the specified factors:
X = [xi * xFactor[n] for xi in RiverData[:,0]]
Y = [yi * yFactor[n] for yi in RiverData[:,1]]
# create sub-plot, here, you need to use n+1,
# because your loop iterable counts from 0,
# but your sub-plots count from 1
ax = plt.subplot(2,4,n+1)
# use the created axis object to plot your data;
# use ax.plot instead of plt.plot
ax.plot(X, Y,'ko')
# specify your axis details, again use ax.plot instead of plt.plot
ax.set_xlabel('River Length (km)')
ax.set_ylabel('Area (Km$^2$)')
# the file name can be used as plot title
# (if you want to omit the .txt ending, use [:-4]
# to omit the last for characters in the title string)
ax.set_title(FILES[n][:-4])
ax.set_xscale('log')
ax.set_yscale('log')
# to avoid surprises going from one loop to the next,
# clear the data from the variables
RiverData = None
ax = None
plt.show()
As Thiru pointed out you do not need to increment your iterable inside a for-loop.
Please check the matplotlib documentation on subplots
http://matplotlib.org/examples/animation/subplots.html
You can create a figure and add multiple subplots to it.
fig = plt.figure()
for plotnums in range(1,9):
plot1 = fig.add_subplot(2,4,plotnums) # update the numbers as required
...
I have a 10 x 10 grid that I would like to remove points outside of a shapely Polygon:
import numpy as np
from shapely.geometry import Polygon, Point
from descartes import PolygonPatch
gridX, gridY = np.mgrid[0.0:10.0, 0.0:10.0]
poly = Polygon([[1,1],[1,7],[7,7],[7,1]])
#plot original figure
fig = plt.figure()
ax = fig.add_subplot(111)
polyp = PolygonPatch(poly)
ax.add_patch(polyp)
ax.scatter(gridX,gridY)
plt.show()
Here is the resulting figure:
And what I want the end result to look like:
I know that I can reshape the array to a 100 x 2 array of grid points:
stacked = np.dstack([gridX,gridY])
reshaped = stacked.reshape(100,2)
I can see if the point lies within the polygon easily:
for i in reshaped:
if Point(i).within(poly):
print True
But I am having trouble taking this information and modifying the original grid
You're pretty close already; instead of printing True, you could just append the points to a list.
output = []
for i in reshaped:
if Point(i).within(poly):
output.append(i)
output = np.array(output)
x, y = output[:, 0], output[:, 1]
It seems that Point.within doesn't consider points that lie on the edge of the polygon to be "within" it though.