Change color of lineplot mid-line segment - python

I need to plot a line plot. I want to plot all parts of the lineplot that are below zero blue, and all parts above red.
Here's what I managed so far:
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
x = np.linspace(0, 1, 40)
y = np.random.random(len(x))-0.5
da = xr.DataArray(y, dims=('x',), coords={'x':x})
fig = plt.figure(figsize=(12,6))
ax = fig.add_subplot(1, 1, 1)
da.plot(ax=ax, color='red', linewidth=3)
da.where(y<0).plot(ax=ax, color='blue', linewidth=3)
plt.show()
Here's what I get with this script:
But what I want is for the color to change at the threshold of 0, like this example (that I've modified to show what I want):
I've looked at some suggestions here, for example this here: Plot: color all larger than different color
But I get the same figure with that solution. It seems that the solution lies in the fact that all their line segments are incredibly short, so you don't notice that a segment that passes the threshold doesn't change color at the threshold, and only the next segment is drawn in a different color.
Is there a straightforward way to do this? Or do I have to separate the line segments that cross the threshold manually?
Thank you

It seems that the solution lies in the fact that all their line segments are incredibly short, so you don't notice that a segment that passes the threshold doesn't change color at the threshold, and only the next segment is drawn in a different color.
You could just interpolate your data such that this holds true for your data as well.
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
xx = np.linspace(0, 1, 40)
yy = np.random.random(len(xx))-0.5
x = np.linspace(0, 1, 4000)
y = np.interp(x, xx, yy) # linear piecewise interpolation
da = xr.DataArray(y, dims=('x',), coords={'x':x})
fig = plt.figure(figsize=(12,6))
ax = fig.add_subplot(1, 1, 1)
da.plot(ax=ax, color='red', linewidth=3)
da.where(y<0).plot(ax=ax, color='blue', linewidth=3)
plt.show()

Related

How to plot a contour plot if the difference between Zmax and Zmin is of the order of 10^(-5)?

I want to produce a ramachandran plot which would look like the following
basically it is a superposition of two plots: contour and scatter. I have the data file for plotting the contour and scatter plot. The data for contour plot is present as three different columns denoting x, y and z values. the value of x and y varies from -180 to 180. Whereas the value z varies from 0 to 1 and the difference between z values can be as low as 10^(-5). In my code I tried to plot the contour using tricontourf where the difference each entry of the level is 0.01. Whenever I tried to make gap between those levels to 0.00001, the code just doesn't get over. That's why I am unable to generate a graph that I want.
The code that I wrote is the following:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as tri
import matplotlib.cm as cm
x=[]
y=[]
z=[]
x1=[]
y1=[]
lst = []
plt.style.use('seaborn-whitegrid')
for line in open('rama_data.txt', 'r'):
values = [float(s) for s in line.split()]
x.append(values[0])
y.append(values[1])
z.append(values[2])
f=open('all_str_C-S-S-C_Acceptor.txt',"r")
lines=f.readlines()
for m in lines:
x1.append(m.split(' ')[8])
y1.append(m.split(' ')[9])
f.close()
norm = cm.colors.Normalize(vmax=max(z), vmin=min(z))
cmap = cm.OrRd
fig2, ax2 = plt.subplots()
#ax2.set_aspect('equal')
levels = np.arange(0, 1,0.01)
tcf = ax2.tricontourf(x, y, z, levels, cmap=cm.get_cmap(cmap, len(levels)-1),norm=norm)
ax2.set_xticks(np.arange(-180,181,45))
ax2.set_yticks(np.arange(-180,181,45))
ax2.set_xlabel('$\Phi$ Dihedral angle($\circ$)', fontsize=12, fontweight='bold')
ax2.set_ylabel('$\Psi\'$ Dihedral angle($\circ$)', fontsize=12, fontweight='bold')
#cbar=fig2.colorbar(tcf)
#cbar.ax.set_ylabel('Relative Electronic energy(kJ/mol)', fontsize=12, fontweight='bold')
ax2.autoscale(False) # To avoid that the scatter changes limits
ax2.scatter(x1,y1,s=0.15,c='black',zorder=1)
fig2.savefig("Ramachandran plot",dpi=300)
plt.show()
My code generates an image which looks this this:
What modifications should I do do produce the desirable plot?
I have attached the rama_data.txt file. Anyone can download and try it once.
The main problem seems to be that for 100 levels (as in levels = np.arange(0, 1,0.01)) the colors get very smoothed out. Just reducing the number of levels gets a plot much closer to the example plot.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as tri
xyz = np.loadtxt('rama.txt')
x = xyz[:, 0]
y = xyz[:, 1]
z = xyz[:, 2]
fig2, (ax1, ax2) = plt.subplots(ncols=2)
cmap = 'OrRd'
tcf = ax2.tricontourf(x, y, z, levels=5, cmap=cmap) # norm=norm)
filter = (z > 0.2) & (np.random.randint(0, 10, z.size) == 0)
ax2.scatter(x[filter], y[filter], marker='.', s=1, color='black')
ax1.scatter(x, y, c=z, cmap=cmap)
ax1.set_xticks(np.arange(-180, 181, 45))
ax1.set_yticks(np.arange(-180, 181, 45))
ax2.set_xticks(np.arange(-180, 181, 45))
ax2.set_yticks(np.arange(-180, 181, 45))
plt.show()
The plot shows a regular scatter plot of the given data at the left, and the contourf plot at the right.

Remove line through points in matplotlib plot

I have the following matplotlib snippet:
fig, ax = plt.subplots(figsize=(6,6))
values = np.random.normal(loc=0, scale=1, size=10)
ax.plot(range(10), values, 'r^', markersize=15, alpha=0.4);
which produces
as planned.
I'd like to make the line invisible where it overlaps with the points so that the points look more joined by the line rather than lying on top of the line. It is possible to do this by either making the line invisible where they overlap or to create a new line object that simply links the points rather than traces them?
To be explicit, I do not want the entire line removed, just the sections that overlap with the points.
It is in general hard to let the lines stop at the edges of the markers. The reason is that lines are defined in data coordinates, while the markers are defined in points.
A workaround would be to hide the lines where the markers are. We may think of a three layer system. The lowest layer (zorder=1) contains the lines, just as they are. The layer above contains markers of the same shape and size as those which are to be shown. Yet they would be colored in the same color as the background (usually white). The topmost layer contains the markers as desired.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(42)
fig, ax = plt.subplots(figsize=(6,5))
def plot_hidden_lines(x,y, ax = None, ms=15, color="r",
marker="^", alpha=0.4,**kwargs):
if not ax: ax=plt.gca()
ax.scatter(x,y, c=color, s=ms**2, marker=marker, alpha=alpha, zorder=3)
ax.scatter(x,y, c="w", s=ms**2, marker=marker, alpha=1, zorder=2)
ax.plot(x,y, color=color, zorder=1,alpha=alpha,**kwargs)
values1 = np.random.normal(loc=0, scale=1, size=10)
values2 = np.random.normal(loc=0, scale=1, size=10)
x = np.arange(len(values1))
plot_hidden_lines(x,values1)
plot_hidden_lines(x,values2, color="indigo", ms=20, marker="s")
plt.show()
I think the best way to go about it is to overlay the triangles over the lines:
import matplotlib.pyplot as plt
import numpy as np
values = np.random.normal(loc=0, scale=1, size=10)
plt.plot( range(10), values, marker='^', markerfacecolor='red', markersize=15, color='red', linewidth=2)
plt.show()
The program outputs:
If you really want the see through aspect, I suggest you somehow calculate where the lines overlap with the markers and only draw the lines inbetween:
import numpy as np
import matplotlib.pyplot as plt
values = np.random.normal(loc= 0, scale=1, size=10)
for i in range(9):
start_coordinate, end_coordinate = some_function(values[i], values[i+1])
plt.plot([i, i+1], [start_coordinate, end_coordinate], *whatever_other_arguments)
plt.scatter(range(10), values, *whatever_other_arguments)
plt.show()
The hard part here is of course calculating these coordinates (if you want to zoom in this won't work), but honestly, given the difficulty of this question, I think you won't find anything much better...

How to get differents colors in a single line in a Matplotlib figure?

I am using matplotlib to create the plots. I have to draw a line in a chart which color must be defined in function of each point. For example, I need a line where the points under 2000 are painted red, and points above 2000 are painted blue. How can I get this ? Do you know a similar solution or workaround to achieve it?
This is my sample code, which paint the hole line blue (default color I guess)
def draw_curve(points, labels):
plt.figure(figsize=(12, 4), dpi=200)
plt.plot(labels,points)
filename = "filename.png"
plt.savefig("tmp/{0}".format(filename))
figure = plt.figure()
plt.close(figure)
So, in the image below, I would like that values above the light blue horizontal line were painted in a different color than under values.
Thanks in advance.
You have to color every segment of your line:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
# my func
x = np.linspace(0, 2 * np.pi, 100)
y = 3000 * np.sin(x)
# select how to color
cmap = ListedColormap(['r','b'])
norm = BoundaryNorm([2000,], cmap.N)
# get segments
xy = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.hstack([xy[:-1], xy[1:]])
# make line collection
lc = LineCollection(segments, cmap = cmap, norm = norm)
lc.set_array(y)
# plot
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
plt.show()
More examples here: http://matplotlib.org/examples/pylab_examples/multicolored_line.html

Python matplotlib Colorfunction

I would like to use a ColorFunction similar to that in Mathematica for my plots in python.
In other words, I would like to call pyplot.plot(x, y, color=c), where c is a vector, defining the color of each data point.
Is there any way to achieve this using the matplotlib library?
To the best of my knowledge, there is no equivalent in Matplotlib, but we can get the similar result following two steps: draw points with varied colors and draw the line.
Here is a demo.
The source code,
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import random
fig, ax = plt.subplots()
nrof_points = 100
x = np.linspace(0, 10, nrof_points)
y = np.sin(x)
colors = cm.rainbow(np.linspace(0, 1, nrof_points)) # generate a bunch of colors
# draw points
for idx, point in enumerate(zip(x, y)):
ax.plot(point[0], point[1], 'o', color=colors[idx], markersize=10)
# draw the line
ax.plot(x, y, 'k')
plt.grid()
plt.show()
While I agree with #SparkAndShine that there is no way to parameterize the color of one line, it is possible to color many lines to create a visual effect that is largely the same. This is at the heart of a demo in the MatPlotLib documentation. However, this demo is not the simplest implementation of this principle. Here is an alternate demo based on #SparkAndShine's response:
colored sine (can't post as image since I don't have the reputation)
Source code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
fig, ax = plt.subplots()
nrof_points = 100
x = np.linspace(0, 10, nrof_points)
y = np.sin(x)
colors = cm.rainbow(np.linspace(0, 1, nrof_points)) # generate a bunch of colors
# draw points
for idx in range(0,np.shape(x)[0]-2,1):
ax.plot(x[idx:idx+1+1], y[idx:idx+1+1], color=colors[idx])
# add a grid and show
plt.grid()
plt.show()

Understanding Matplotlib's quiver plotting

I'm trying to understand how plt.quiver() works. My issue is as follows:
I plot a simple vector (1,1) as such:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure(2)
ax = fig.add_subplot(111)
ax.quiver(0,0, 1, 1, units = 'xy', scale = 1)
plt.xticks(range(-5,6))
plt.yticks(range(-5,6))
plt.grid()
I would expect the arrow to go from (0,0) to (1,1), but the result is slightly off from that:
Similarly, I try and plot an arrow for vector (0,3) and the resulting arrow seems to be for vector (0,3.5)...
My assumption is that this has something to do with the kwargs 'units', 'scale', 'angles', & 'scale_units'. I've read the docs on them but don't fully understand how they work. A sunday school explanation would be greatly appreciated!
If you adjust the aspect ratio of the figure to 1, the vectors are displayed to proper scale:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.quiver((0,0), (0,0), (1,0), (1,3), units = 'xy', scale = 1)
plt.axis('equal')
plt.xticks(range(-5,6))
plt.yticks(range(-5,6))
plt.grid()
plt.show()
You can try this code.
import matplotlib.pyplot as plt
fig = plt.figure(2)
ax = fig.add_subplot(111)
ax.quiver(0,0, 1, 1,angles='xy', scale_units='xy', scale = 1)
plt.xticks(range(-5,6))
plt.yticks(range(-5,6))
plt.grid()
plt.draw()
plt.show()
Just remember that the first two arguments of quiver are the x and y coordinates of the tail of the vector, the next two are the lengths of the vector along x and y direction respectively. angle='xy' makes the arrow point from tail of the vector to its tip.
You can find out more about matplotlib.quiver at http://matplotlib.org/1.3.1/api/pyplot_api.html#matplotlib.pyplot.quiver

Categories