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
...
Related
Let's assume I have 3 arrays defined as:
v1=np.linspace(1,100)
v2=np.linspace(1,100)
v3=np.linspace(1,100)
Then I have a function that takes those 3 values and gives me the desired output, let's assume it is like:
f = (v1 + v2*10)/v3
I want to plot that function on a 3D plot with axis v1,v2,v3 and color it's surface depending on its value.
More than the best way to plot it, I was also interested in how to scroll all the values in the in vectors and build the function point by point.
I have been trying with for loops inside other for loops but I am always getting one error.
MANY THANKS
I tried this but i'm always getting a line instead of a surface
import mpl_toolkits.mplot3d.axes3d as axes3d
import sympy
from sympy import symbols, Function
# Parameters I use in the function
L = 132
alpha = 45*math.pi/180
beta = 0
s,t = symbols('s,t')
z = Function('z')(s,t)
figure = plt.figure(figsize=(8,8))
ax = figure.add_subplot(1, 1, 1, projection='3d')
# experiment with various range of data in x and y
x1 = np.linspace(-40,-40,100)
y1 = np.linspace(-40,40,100)
x,y = np.meshgrid(x1,y1)
# My function Z
c1=math.cos(beta)**2
c2=math.cos(alpha)**2
s1=math.sin(alpha)**2
den = math.sqrt((c1*c2)+s1)
z=L*((math.cos(beta)/den)-1)+(s*(math.sin(alpha)))+(t*(1-math.cos(alpha)))
ax.plot_surface(x,y,z,cmap='rainbow')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
In this example I'm going to show you how to achieve your goal. Specifically, I use Numpy because it supports vectorized operations, hence I avoid for loops.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import matplotlib.cm as cm
# Parameters I use in the function
L = 132
alpha = 45*np.pi/180
beta = 0
figure = plt.figure()
ax = figure.add_subplot(1, 1, 1, projection='3d')
# experiment with various range of data in x and y
x1 = np.linspace(-40,40,100)
y1 = np.linspace(-40,40,100)
x,y = np.meshgrid(x1,y1)
# My function Z
c1=np.cos(beta)**2
c2=np.cos(alpha)**2
s1=np.sin(alpha)**2
den = np.sqrt((c1*c2)+s1)
z=L*((np.cos(beta)/den)-1)+(x*(np.sin(alpha)))+(y*(1-np.cos(alpha)))
# compute the color values according to some other function
color_values = np.sqrt(x**2 + y**2 + z**2)
# normalize color values between 0 and 1
norm = Normalize(vmin=color_values.min(), vmax=color_values.max())
norm_color_values = norm(color_values)
# chose a colormap and create colors starting from the normalized values
cmap = cm.rainbow
colors = cmap(norm_color_values)
surf = ax.plot_surface(x,y,z,facecolors=colors)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
# add a colorbar
figure.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), label="radius")
plt.show()
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!
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 would like to plot labels on a line of a lineplot in matplotlib.
Minimal example
#!/usr/bin/env python
import numpy as np
import seaborn as sns
sns.set_style("whitegrid")
sns.set_palette(sns.color_palette("Greens", 8))
from scipy.ndimage.filters import gaussian_filter1d
for i in range(8):
# Create data
y = np.roll(np.cumsum(np.random.randn(1000, 1)),
np.random.randint(0, 1000))
y = gaussian_filter1d(y, 10)
sns.plt.plot(y, label=str(i))
sns.plt.legend()
sns.plt.show()
generates
instead, I would prefer something like
Maybe a bit hacky, but does this solve your problem?
#!/usr/bin/env python
import numpy as np
import seaborn as sns
sns.set_style("whitegrid")
sns.set_palette(sns.color_palette("Greens", 8))
from scipy.ndimage.filters import gaussian_filter1d
for i in range(8):
# Create data
y = np.roll(np.cumsum(np.random.randn(1000, 1)),
np.random.randint(0, 1000))
y = gaussian_filter1d(y, 10)
p = sns.plt.plot(y, label=str(i))
color = p[0].get_color()
for x in [250, 500, 750]:
y2 = y[x]
sns.plt.plot(x, y2, 'o', color='white', markersize=9)
sns.plt.plot(x, y2, 'k', marker="$%s$" % str(i), color=color,
markersize=7)
sns.plt.legend()
sns.plt.show()
Here's the result I get:
Edit: I gave it a little more thought and came up with a solution that automatically tries to find the best possible position for the labels in order to avoid the labels being positioned at x-values where two lines are very close to each other (which could e.g. lead to overlap between the labels):
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
sns.set_style("whitegrid")
sns.set_palette(sns.color_palette("Greens", 8))
from scipy.ndimage.filters import gaussian_filter1d
# -----------------------------------------------------------------------------
def inline_legend(lines, n_markers=1):
"""
Take a list containing the lines of a plot (typically the result of
calling plt.gca().get_lines()), and add the labels for those lines on the
lines themselves; more precisely, put each label n_marker times on the
line.
[Source of problem: https://stackoverflow.com/q/43573623/4100721]
"""
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
from math import fabs
def chunkify(a, n):
"""
Split list a into n approximately equally sized chunks and return the
indices (start/end) of those chunks.
[Idea: Props to http://stackoverflow.com/a/2135920/4100721 :)]
"""
k, m = divmod(len(a), n)
return list([(i * k + min(i, m), (i + 1) * k + min(i + 1, m))
for i in range(n)])
# Calculate linear interpolations of every line. This is necessary to
# compare the values of the lines if they use different x-values
interpolations = [interp1d(_.get_xdata(), _.get_ydata())
for _ in lines]
# Loop over all lines
for idx, line in enumerate(lines):
# Get basic properties of the current line
label = line.get_label()
color = line.get_color()
x_values = line.get_xdata()
y_values = line.get_ydata()
# Get all lines that are not the current line, as well as the
# functions that are linear interpolations of them
other_lines = lines[0:idx] + lines[idx+1:]
other_functions = interpolations[0:idx] + interpolations[idx+1:]
# Split the x-values in chunks to get regions in which to put
# labels. Creating 3 times as many chunks as requested and using only
# every third ensures that no two labels for the same line are too
# close to each other.
chunks = list(chunkify(line.get_xdata(), 3*n_markers))[::3]
# For each chunk, find the optimal position of the label
for chunk_nr in range(n_markers):
# Start and end index of the current chunk
chunk_start = chunks[chunk_nr][0]
chunk_end = chunks[chunk_nr][1]
# For the given chunk, loop over all x-values of the current line,
# evaluate the value of every other line at every such x-value,
# and store the result.
other_values = [[fabs(y_values[int(x)] - f(x)) for x in
x_values[chunk_start:chunk_end]]
for f in other_functions]
# Now loop over these values and find the minimum, i.e. for every
# x-value in the current chunk, find the distance to the closest
# other line ("closest" meaning abs_value(value(current line at x)
# - value(other lines at x)) being at its minimum)
distances = [min([_ for _ in [row[i] for row in other_values]])
for i in range(len(other_values[0]))]
# Now find the value of x in the current chunk where the distance
# is maximal, i.e. the best position for the label and add the
# necessary offset to take into account that the index obtained
# from "distances" is relative to the current chunk
best_pos = distances.index(max(distances)) + chunks[chunk_nr][0]
# Short notation for the position of the label
x = best_pos
y = y_values[x]
# Actually plot the label onto the line at the calculated position
plt.plot(x, y, 'o', color='white', markersize=9)
plt.plot(x, y, 'k', marker="$%s$" % label, color=color,
markersize=7)
# -----------------------------------------------------------------------------
for i in range(8):
# Create data
y = np.roll(np.cumsum(np.random.randn(1000, 1)),
np.random.randint(0, 1000))
y = gaussian_filter1d(y, 10)
sns.plt.plot(y, label=str(i))
inline_legend(plt.gca().get_lines(), n_markers=3)
sns.plt.show()
Example output of this solution (note how the x-positions of the labels are no longer all the same):
If one wants to avoid the use of scipy.interpolate.interp1d, one might consider a solution where for a given x-value of line A, one finds the x-value of line B that is closest to that. I think this might be problematic though if the lines use very different and/or sparse grids?
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.