I am currently using the following code to plot a graph using python pyplot:
plt.plot([row[2] for row in data],[row[1] for row in data], type, marker='o', label=name)
However, instead of the default marker of 'o' I want the marker at the points to be the data in row[1]
Can someone explain how to do this?
So you want to annotate the y-values of the points along your line?
Use annotate for each point. For example:
import matplotlib.pyplot as plt
x = range(10)
y = range(10)
fig, ax = plt.subplots()
# Plot the line connecting the points
ax.plot(x, y)
# At each point, plot the y-value with a white box behind it
for xpoint, ypoint in zip(x, y):
ax.annotate('{:.2f}'.format(ypoint), (xpoint,ypoint), ha='center',
va='center', bbox=dict(fc='white', ec='none'))
# Manually tweak the limits so that our labels are inside the axes...
ax.axis([min(x) - 1, max(x) + 1, min(y) - 1, max(y) + 1])
plt.show()
Related
When I create a plot with many curves it would be convenient to be able to label each curve at the right where it ends.
The result of plt.legend produces too many similar colors and the legend is overlapping the plot.
As one can see in the example below the use of plt.legend is not very effective:
import numpy as np
from matplotlib import pyplot as plt
n=10
x = np.linspace(0,1, n)
for i in range(n):
y = np.linspace(x[i],x[i], n)
plt.plot(x, y, label=str(i))
plt.legend(loc='upper right')
plt.show()
If possible I would like to have something similar to this plot:
or this:
I would recommend the answer suggested in the comments, but another method that gives something similar to your first option (albeit without the exact placement of the legend markers matching the positions of the associated lines) is:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
n=10
x = np.linspace(0, 1, n)
labels = [str(i) for i in range(len(x))]
for i in range(n):
y = np.linspace(x[i], x[i], n)
ax.plot(x, y, label=labels[i])
h, _ = ax.get_legend_handles_labels()
# sort the legend handles/labels so they are in the same order as the data
hls = sorted(zip(x, h, labels), reverse=True)
ax.legend(
[ha[1] for ha in hls], # get handles
[la[2] for la in hls], # get labels
bbox_to_anchor=(1.04, 0, 0.1, 1), # set box outside of axes
loc="lower left",
labelspacing=1.6, # add space between labels
)
leg = ax.get_legend()
# expand the border of the legend
fontsize = fig.canvas.get_renderer().points_to_pixels(leg._fontsize)
pad = 2 * (leg.borderaxespad + leg.borderpad) * fontsize
leg._legend_box.set_height(leg.get_bbox_to_anchor().height - pad)
This is heavily reliant on the answers here and here.
How can I make that when I plot a function (based on a np.array) certain values have their coordinates in the plot?
I know how to change color and other little things with code lines like:
line1, = plt.plot(t, f, '*-', label='force', color='#4F81BD') # blue
line2, = plt.plot(t, a, 'o-', label='acceleration', color='#C0504D') # red
but for example if I have a "peak" in the plot line, I don't know how to make their coordinates to appear in the same plot
This code snippet might help you:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
x=[1,2,3,4,5,6,7,8,9,10]
y=[1,1,1,2,10,2,1,1,1,1]
line, = ax.plot(x, y)
ymax = max(y)
xpos = y.index(ymax)
xmax = x[xpos]
#Labeling the graph (ymax+1 is defining the distance from the word to the point)
ax.annotate('local max', xy=(xmax, ymax), xytext=(xmax, ymax+1))
ax.set_ylim(0,20)
plt.show()
Output:
I hope I could help you out a bit.
how to add labels to a horizontal bar chart in matplotlib?
Hi everyone, I'm a matplotlib and python newbie and I wanted to ask this question again to get a bit of help as to if there are easier ways to add labels for the count represented by each bar than the current solutions I've found.
Here is the code I have written:
from matplotlib.pyplot import figure
figure(num=None, figsize=(8, 24), dpi=80, facecolor='w', edgecolor='k')
df['Name'].value_counts()[:80].plot(kind='barh')
It works just fine, except for the showing labels next to the bars bit...
I looked on here how to add the label and so I change my code to this:
x = df['Name']
y = df['Name'].value_counts(ascending=True)
fig, ax = plt.subplots(figsize=(18,20))
width = 0.75 # the width of the bars
ind = np.arange(len(y)) # the x locations for the groups
ax.barh(ind, y, width, color="blue")
ax.set_yticks(ind+width/2)
ax.set_yticklabels(y, minor=False)
plt.title('Count of supplies')
plt.xlabel('Count')
plt.ylabel('ylabel')
for i, v in enumerate(y):
ax.text(v + 100, i + 0, str(v), color='black', fontweight='bold')
However, now my names aren't associated with the bars and are just like in order they appear within the dataframe. is there a way to just simply change the first code or to make it so the names associated with bars are correct in 2nd attempt (grouped with the bar they are labeling..)?
Image sorta explaining my issue:
Using the index of y as the index of the barh plot should put the y-labels on the correct spot, next to the corresponding bar. There's no need to manipulate the y-ticklabels. The bar labels can be left aligned and vertically centered. The right x-limit may be moved a bit to have room for the label of the longest bar.
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
df = pd.DataFrame({'Name': np.random.choice(list('AABBBBBCCCCCDEEF'), 20000)})
y = df['Name'].value_counts(ascending=False)
fig, ax = plt.subplots(figsize=(12,5))
ax.barh(y.index, y, height=0.75, color="slateblue")
plt.title('Count of supplies')
plt.xlabel('Count')
plt.ylabel('ylabel')
_, xmax = plt.xlim()
plt.xlim(0, xmax+300)
for i, v in enumerate(y):
ax.text(v + 100, i, str(v), color='black', fontweight='bold', fontsize=14, ha='left', va='center')
plt.show()
I have two lists containing the x and y coordinates of some points. There is also a list with some values assigned to each of those points. Now my question is, I can always plot the points (x,y) using markers in python. Also I can select colour of the marker manually (as in this code).
import matplotlib.pyplot as plt
x=[0,0,1,1,2,2,3,3]
y=[-1,3,2,-2,0,2,3,1]
colour=['blue','green','red','orange','cyan','black','pink','magenta']
values=[2,6,10,8,0,9,3,6]
for i in range(len(x)):
plt.plot(x[i], y[i], linestyle='none', color=colour[i], marker='o')
plt.axis([-1,4,-3,4])
plt.show()
But is it possible to choose a colour for the marker marking a particular point according to the value assigned to that point (using cm.jet, cm.gray or similar other color schemes) and provide a colorbar with the plot ?
For example, this is the kind of plot I am looking for
where the red dots denote high temperature points and the blue dots denote low temperature ones and others are for temperatures in between.
You are most likely looking for matplotlib.pyplot.scatter. Example:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
# Generate data:
N = 10
x = np.linspace(0, 1, N)
y = np.linspace(0, 1, N)
x, y = np.meshgrid(x, y)
colors = np.random.rand(N, N) # colors for each x,y
# Plot
circle_size = 200
cmap = matplotlib.cm.viridis # replace with your favourite colormap
fig, ax = plt.subplots(figsize=(4, 4))
s = ax.scatter(x, y, s=circle_size, c=colors, cmap=cmap)
# Prettify
ax.axis("tight")
fig.colorbar(s)
plt.show()
Note: viridis may fail on older version of matplotlib.
Resulting image:
Edit
scatter does not require your input data to be 2-D, here are 4 alternatives that generate the same image:
import matplotlib
import matplotlib.pyplot as plt
x = [0,0,1,1,2,2,3,3]
y = [-1,3,2,-2,0,2,3,1]
values = [2,6,10,8,0,9,3,6]
# Let the colormap extend between:
vmin = min(values)
vmax = max(values)
cmap = matplotlib.cm.viridis
norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)
fig, ax = plt.subplots(4, sharex=True, sharey=True)
# Alternative 1: using plot:
for i in range(len(x)):
color = cmap(norm(values[i]))
ax[0].plot(x[i], y[i], linestyle='none', color=color, marker='o')
# Alternative 2: using scatter without specifying norm
ax[1].scatter(x, y, c=values, cmap=cmap)
# Alternative 3: using scatter with normalized values:
ax[2].scatter(x, y, c=cmap(norm(values)))
# Alternative 4: using scatter with vmin, vmax and cmap keyword-arguments
ax[3].scatter(x, y, c=values, vmin=vmin, vmax=vmax, cmap=cmap)
plt.show()
I'm trying to shade points in a scatter plot based on a set of values (from 0 to 1) picked from one of the already defined color maps, like Blues or Reds. I tried this:
import matplotlib
import matplotlib.pyplot as plt
from numpy import *
from scipy import *
fig = plt.figure()
mymap = plt.get_cmap("Reds")
x = [8.4808517662594909, 11.749082788323497, 5.9075039082855652, 3.6156231827873615, 12.536817102137768, 11.749082788323497, 5.9075039082855652, 3.6156231827873615, 12.536817102137768]
spaced_colors = linspace(0, 1, 10)
print spaced_colors
plt.scatter(x, x,
color=spaced_colors,
cmap=mymap)
# this does not work either
plt.scatter(x, x,
color=spaced_colors,
cmap=plt.get_cmap("gray"))
But it does not work, using either the Reds or gray color map. How can this be done?
edit: if I want to plot each point separately so it can have a separate legend, how can I do it? I tried:
fig = plt.figure()
mymap = plt.get_cmap("Reds")
data = np.random.random([10, 2])
colors = list(linspace(0.1, 1, 5)) + list(linspace(0.1, 1, 5))
print "colors: ", colors
plt.subplot(1, 2, 1)
plt.scatter(data[:, 0], data[:, 1],
c=colors,
cmap=mymap)
plt.subplot(1, 2, 2)
# attempt to plot first five points in five shades of red,
# with a separate legend for each point
for n in range(5):
plt.scatter([data[n, 0]], [data[n, 1]],
c=[colors[n]],
cmap=mymap,
label="point %d" %(n))
plt.legend()
but it fails. I need to make a call to scatter for each point so that it can have a separate label=, but still want each point to have a different shade of the color map as its color.
thanks.
If you really want to do this (what you describe in your edit), you have to "pull" the colors from your colormap (I have commented all changes I made to your code):
import numpy as np
import matplotlib.pyplot as plt
# plt.subplots instead of plt.subplot
# create a figure and two subplots side by side, they share the
# x and the y-axis
fig, axes = plt.subplots(ncols=2, sharey=True, sharex=True)
data = np.random.random([10, 2])
# np.r_ instead of lists
colors = np.r_[np.linspace(0.1, 1, 5), np.linspace(0.1, 1, 5)]
mymap = plt.get_cmap("Reds")
# get the colors from the color map
my_colors = mymap(colors)
# here you give floats as color to scatter and a color map
# scatter "translates" this
axes[0].scatter(data[:, 0], data[:, 1], s=40,
c=colors, edgecolors='None',
cmap=mymap)
for n in range(5):
# here you give a color to scatter
axes[1].scatter(data[n, 0], data[n, 1], s=40,
color=my_colors[n], edgecolors='None',
label="point %d" %(n))
# by default legend would show multiple scatterpoints (as you would normally
# plot multiple points with scatter)
# I reduce the number to one here
plt.legend(scatterpoints=1)
plt.tight_layout()
plt.show()
However, if you only want to plot 10 values and want to name every single one,
you should consider using something different, for instance a bar chart as in this
example. Another opportunity would be to use plt.plot with a custom color cycle, like in this example.
As per the documentation, you want the c keyword argument instead of color. (I agree that this is a bit confusing, but the "c" and "s" terminology is inherited from matlab, in this case.)
E.g.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
x, y, colors = np.random.random((3,10))
fig, ax = plt.subplots()
ax.scatter(x, y, c=colors, s=50, cmap=mpl.cm.Reds)
plt.show()
How about:
import matplotlib.pyplot as plt
import numpy as np
reds = plt.get_cmap("Reds")
x = np.linspace(0, 10, 10)
y = np.log(x)
# color by value given a cmap
plt.subplot(121)
plt.scatter(x, y, c=x, s=100, cmap=reds)
# color by value, and add a legend for each
plt.subplot(122)
norm = plt.normalize()
norm.autoscale(x)
for i, (x_val, y_val) in enumerate(zip(x, y)):
plt.plot(x_val, y_val, 'o', markersize=10,
color=reds(norm(x_val)),
label='Point %s' % i
)
plt.legend(numpoints=1, loc='lower right')
plt.show()
The code should all be fairly self explanatory, but if you want me to go over anything, just shout.