Plot vertical arrows for my points - python-2.7

I am trying to figure out a way to add a vertical arrow pointing up for each of my data points. I have scatter plot and code below. I need the vertical arrows to start from the points going upwards to a length of about 0.2 in th graph scale.
import matplotlib.pyplot as plt
fig = plt.figure()
a1 = fig.add_subplot(111)
simbh = np.array([5.3, 5.3, 5.5, 5.6, 5.6, 5.8, 5.9, 6.0, 6.2, 6.3, 6.3])
simstel =np.array([10.02, 10.08, 9.64, 9.53, 9.78, 9.65, 10.05, 10.09, 10.08, 10.22, 10.42])
sca2=a1.scatter(simstel, simbh )

This is bit hacky, adjust arrow_offset and arrow_size until the figure looks right.
import matplotlib.pyplot as plt
fig = plt.figure()
a1 = fig.add_subplot(111)
simbh = np.array([5.3, 5.3, 5.5, 5.6, 5.6, 5.8, 5.9, 6.0, 6.2, 6.3, 6.3])
simstel =np.array([10.02, 10.08, 9.64, 9.53, 9.78, 9.65, 10.05, 10.09, 10.08, 10.22, 10.42])
sca2=a1.scatter(simstel, simbh, c='w' )
arrow_offset = 0.08
arrow_size = 500
sca2=a1.scatter(simstel, simbh + arrow_offset,
marker=r'$\uparrow$', s=arrow_size)

The other approaches presented are great. I'm going for the hackiest award today:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
simbh = np.array([5.3, 5.3, 5.5, 5.6, 5.6, 5.8, 5.9, 6.0, 6.2, 6.3, 6.3])
simstel = np.array([10.02, 10.08, 9.64, 9.53, 9.78, 9.65, 10.05, 10.09, 10.08, 10.22, 10.42])
sca2 = ax.scatter(simstel, simbh)
for x, y in zip(simstel, simbh):
ax.annotate('', xy=(x, y), xytext=(0, 25), textcoords='offset points',
arrowprops=dict(arrowstyle="<|-"))

This is not super elegant, but it does the trick
to get the arrows start at the data point and go up 0.2 units:
for x,y in zip(simstel,simbh):
plt.arrow(x,y,0,0.2)

This can be done directly
from matplotlib import pyplot as plt
import numpy as np
# set up figure
fig, ax = plt.subplots()
# make synthetic data
x = np.linspace(0, 1, 15)
y = np.random.rand(15)
yerr = np.ones_like(x) * .2
# if you are using 1.3.1 or older you might need to use uplims to work
# around a bug, see below
ax.errorbar(x, y, yerr=yerr, lolims=True, ls='none', marker='o')
# adjust axis limits
ax.margins(.1) # margins makes the markers not overlap with the edges
There was some strangeness in how these arrows are implemented where the semantics changed so that 'lolims' means 'the data point is the lower limit' and 'uplims' means 'the data point is the maximum value'.
See https://github.com/matplotlib/matplotlib/pull/2452

Related

Data format for gnuplot plot

I have data file coming from a fortran code. The data are 2 arrays v and np of size 500 and a scalar time.
Each time I append to a file the new time value and the 2 vectors in 2 new lines of the file, in this format:
time, v(1), v(2), v(3), ..., v(499), v(500)
time, np(1), np(2), np(3), ..., np(499), np(500)
For example:
0.0, 1.0, 2.0, 3.0, ..., 499.0, 500.0
0.0, 0.1, 0.2, 0.3, ..., 0.499, 0.500
1.0, 1.0, 2.0, 3.0, ..., 499.0, 500.0
1.0, 0.1, 0.2, 0.3, ..., 0.499, 0.500
2.0, 1.0, 2.0, 3.0, ..., 499.0, 500.0
2.0, 0.1, 0.2, 0.3, ..., 0.499, 0.500
What i want, is to plot np as a function of v at an specific time (So in this case if i want time=2 i will plot lines 5 and 6 with ignoring the first row). However gnuplot doesn't like this format. I made it work using python, however i must do it with gnuplot.
I searched online and found that i can output my data in another format but this doesn't work properly either. This format looks like this:
0.0 0.0
1.0 0.1
2.0 0.2
3.0 0.3
4.0 0.4
... ...
499.0 0.499
500.0 0.500
1.0 1.0
1.0 0.1
2.0 0.2
3.0 0.3
4.0 0.4
... ...
499.0 0.499
500.0 0.500
2.0 2.0
1.0 0.1
2.0 0.2
3.0 0.3
4.0 0.4
... ...
499.0 0.499
500.0 0.500
This format plots everything including the time and even using for loops and every function it doesn't work.
I also searched if I could format my data in columns in fortran, however i couldn't find any solution to that. The problem i have is that at each time, the arrays v and np are erased from memory. And for reasons I can't explain, I can't save v and np in a matrix and save them for later.
Is there a way i can format my data on fortran to read it on gnuplot and be able to plot only one time ?
Or is there a way i can read this format using only gnuplot ?
gnuplot doesn't like data in rows.
You could transpose your data with an external tool and simply plot it as columns with gnuplot. Unfortunately, gnuplot has no transpose function itself, although, in principle you could also transpose with gnuplot (https://stackoverflow.com/a/65576405/7295599), but it's probably highly inefficient for large datasets.
Actually, here is an awkward gnuplot solution to plot data from two rows.
The values of the rows of interest are stored in arrays (hence requires gnuplot >=5.2.0) using a dummy table. The option every ::SkipCols skips the first SkipCols columns. In your case SkipCols=1 which skips the time values.
Maybe somebody can simplify this approach further.
Code:
### plotting a row versus another row (requires gnuplot >=5.2.0)
reset session
$Data <<EOD
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7
0.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7
1.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7
1.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7
2.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7
2.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7
EOD
myXrow = 2 # counting starts from 0
myYrow = 3 #
SkipCols = 1 # skip number of columns from the beginning
stats $Data u 0 nooutput # get the number of columns
array X[STATS_columns-SkipCols] # define array size
array Y[STATS_columns-SkipCols] #
myX(row) = ($2==row ? X[$1-SkipCols+1]=$3 : NaN)
myY(row) = ($2==row ? Y[$1-SkipCols+1]=$3 : NaN)
# put the x,y rows into arrays
set table $Dummy
plot $Data matrix u (myX(myXrow),myY(myYrow)) every :myYrow-myXrow:SkipCols:myXrow::myXrow+myYrow w table
unset table
undef $Dummy
set key noautotitle
plot X u 2:(Y[$1]) w lp pt 7
### end of code
Result:
Addition: (Version for gnuplot 5.0)
Here is a version for gnuplot 5.0. Although datablocks have been introduced in gnuplot 5.0 you cannot address them via index as you can do in gnuplot 5.2. So, this workaround uses strings to store the rows and then printing it back into a datablock.
Not very elegant and probably not efficient, but it seems to work.
Unless there would be a limit of the string length, it should also work for your 500 columns. Actually, you have comma as separator, so you have set datafile separator comma and later set datafile separator whitespace. The code can probably still be optimized.
Code: (Result same as above)
### plotting a row versus another row (working for gnuplot 5.0)
reset session
$Data <<EOD
0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7
0.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7
1.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7
1.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7
2.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7
2.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7
EOD
myXrow = 2 # counting starts from 0
myYrow = 3
set datafile separator comma
X = Y = ''
AddValue(S,row) = S.($2==row ? sprintf(" %g",$3) : '')
set table $Dummy
plot $Data matrix u (X=AddValue(X,myXrow),Y=AddValue(Y,myYrow)) every :myYrow-myXrow:1:myXrow::myXrow+myYrow
unset table
undef $Dummy
set print $DataNew
do for [i=1:words(X)] { print sprintf("%s %s",word(X,i),word(Y,i)) }
set print
set datafile separator whitespace
set key noautotitle
plot $DataNew u 1:2 w lp pt 7
### end of code

Setting axes height - stretch axes height to legend height

I have a plot with a legend whose height is bigger than the axes height (like the result of the code below). Now, I would like to stretch the axes height in such a way that it ends at the legend's end.
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0., 10.5, 0.5)
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
ax.legend(bbox_to_anchor=(1.01, 1), loc=2, borderaxespad=0.)
What I would like to have is something like that (it does not matter, if the bottom of the grid or the bottom of the ticks ends with the legend)
I tried to reach my goal with appending following code but without any effect (legend_h is always =1.0):
legend_h = ax.get_legend().get_window_extent().height
ax_height = ax.get_window_extent().height
if ax_height < legend_h:
fig.set_figheight(legend_h/ax_height * fig.get_figheight())
Further on, it would be nice if I could only change the properties of the axes itself and not of the whole figure.
Edit:
My main purpose is to run the figure generation from a script but I also tried it in the Ipython notebook. One try was also to temporarily store the figure before getting the heights and setting the new figure height. But that also did not produce correct results.
I think you can achieve what you want by simply adding plt.draw() to what you already have, e.g.
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
ax.legend(bbox_to_anchor=(1.01, 1), loc=2, borderaxespad=0.)
plt.draw()
legend_h = ax.get_legend().get_window_extent().height
ax_height = ax.get_window_extent().height
if ax_height < legend_h:
fig.set_figheight(legend_h/ax_height * fig.get_figheight())
Update: Also, you can try (which should work from a script, and based on this answer):
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0., 10.5, 0.5)
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
lgd = ax.legend(bbox_to_anchor=(1.01, 1), loc=2, borderaxespad=0.)
fig.tight_layout()
fig.savefig('script.png', bbox_extra_artists=(lgd,), bbox_inches='tight')
In principle, #Matt Pitkin's answer shows the right approach. However, rather than set_figheight one would use set_size_inches. The calculation also needs to include the figure margins, which can be obtained from the fig.subplotpars.
Additionally to the height, we can also set the width of the figure, such that the legend is included.
import matplotlib.pyplot as plt
import numpy as np
t = np.linspace(0,10); c=20
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
bbox = (1.01,1)
ax.legend(bbox_to_anchor=bbox, loc=2, borderaxespad=0.)
fig.canvas.draw()
legend_h = ax.get_legend().get_window_extent().height
ax_height = ax.get_window_extent().height
if ax_height < legend_h:
w,h = fig.get_size_inches()
h =legend_h/fig.dpi/(fig.subplotpars.top-fig.subplotpars.bottom)
fig.set_size_inches(w,h)
# set width as well
w,h = fig.get_size_inches()
r = ax.get_legend().get_window_extent().width/fig.dpi/w
fig.subplots_adjust(right=1-1.1*r)
plt.show()
The picture below is when running this as a script.
In Ipython or jupyter, the figure will automatically be cropped or expanded, because the png shown is automatically saved using the bbox_inches='tight' option. Therefore, the width adjustment is not necessary for a jupyter notebook.

Difference between plt.subplots() and plt.figure()

In python matplotlib, there are two convention used to draw plots:
1.
plt.figure(1,figsize=(400,8))
2.
fig,ax = plt.subplots()
fig.set_size_inches(400,8)
Both have different ways of doing the same thing. eg defining axis label.
Which one is better to use? What is the advantage of one over the other?
Or what is the "Good Practice" for plotting a graph using matplotlib?
Although #tacaswell already gave a brief comment on the key difference. I will add more to this question only from my own experience with matplotlib.
plt.figure just creates a Figure (but with no Axes in it), this means you have to specify the ax to place your data (lines, scatter points, images). Minimum code should look like this:
import numpy as np
import matplotlib.pyplot as plt
# create a figure
fig = plt.figure(figsize=(7.2, 7.2))
# generate ax1
ax1 = fig.add_axes([0.1, 0.1, 0.5, 0.5])
# generate ax2, make it red to distinguish
ax2 = fig.add_axes([0.6, 0.6, 0.3, 0.3], fc='red')
# add data
x = np.linspace(0, 2*np.pi, 20)
y = np.sin(x)
ax1.plot(x, y)
ax2.scatter(x, y)
In the case of plt.subplots(nrows=, ncols=), you will get Figure and an array of Axes(AxesSubplot). It is mostly used for generating many subplots at the same time. Some example code:
def display_axes(axes):
for i, ax in enumerate(axes.ravel()):
ax.text(0.5, 0.5, s='ax{}'.format(i+1), transform=ax.transAxes)
# create figures and (2x2) axes array
fig, axes = plt.subplots(2, 2, figsize=(7.2, 7.2))
# four (2*2=4) axes
ax1, ax2, ax3, ax4 = axes.ravel()
# for illustration purpose
display_axes(axes)
Summary:
plt.figure() is usually used when you want more customization to you axes, such as positions, sizes, colors and etc. You can see artist tutorial for more details. (I personally prefer this for individual plot).
plt.subplots() is recommended for generating multiple subplots in grids. You can also achieve higher flexibility using 'gridspec' and 'subplots', see details here.

matplotlib zorder of elements in polar plot superimposed on cartesian plot

I'm having a difficulty controlling the zorder of the elements of a polar plot superimposed on a cartesian plot.
Consider this example:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(1, 1, marker='*', s=2000, c='r', zorder=2)
ax2 = fig.add_axes(ax.get_position(), frameon=False, polar=True)
ax2.scatter(1., 0.1, marker='*', s=1000, c='b', zorder=1)
plt.xlim(0, 2)
plt.ylim(0, 2)
plt.show()
The result is:
It looks like matplotlib ignored the zorder of scatter plots. I would expect the red star to be on top of the blue one.
Could you please explain what I'm doing wrong here?
I found one question, which is kind of similar to mine, but concerns ticklines and grids instead. Maybe it's the same bug?
P.S. I'm running Linux x86_64 with Python 2.7.6 and matplotlib 1.3.1.
The problem is that you are setting the z-order of the marks on different axes ax and ax2 but since ax2 has a greater z-order all the plots in it will be on top of ax. One solution could be to set a higher z-order to ax but then you need to make the background transparent or set frameon=False (and that's maybe not desirable for your case), this is a demonstration of what I'm saying:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(1, 1, marker='*', s=2000, c='r', zorder=2)
ax2 = fig.add_axes(ax.get_position(), frameon=False, polar=True)
ax2.scatter(1., 0.1, marker='*', s=1000, c='b', zorder=1)
ax.set_zorder(3)
ax.patch.set_facecolor('none')
#ax.patch.set_visible(False)
plt.xlim(0, 2)
plt.ylim(0, 2)
plt.show()
Plot:

Matplotlib Slider Widget and changing colorbar threshold

I am currently trying to work on a program that will allow the user to display their dataset in the form of a colormap and through the use of sliders, it will also allow the user to adjust the threshold of the colormap and thus update the colormap accordingly. The best to describe this would be through the use of a picture:
This image shows how the colorbar should look before (the image on the left) and after (the image on the right) the adjustment. As the threshold values of the colrobar are changed, the colormap would be updated accordingly.
Now I am mainly using matplotlib and I found that matplotlib does support some widgets, such as a slider. However the area I need help in is devising a piece of code which will update the colorbar and colormap (like the way shown in the picture above) when the slider is adjusted. I was wondering if anyone has done this before and might have a piece of code they would be willing to share and might have pointers as to how this can be achieved.
This should get you 80% of the way there:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
ax = plt.subplot(111)
plt.subplots_adjust(left=0.25, bottom=0.25)
img_data = np.random.rand(50,50)
c_min = 0
c_max = 1
img = ax.imshow(img_data, interpolation='nearest')
cb = plt.colorbar(img)
axcolor = 'lightgoldenrodyellow'
ax_cmin = plt.axes([0.25, 0.1, 0.65, 0.03])
ax_cmax = plt.axes([0.25, 0.15, 0.65, 0.03])
s_cmin = Slider(ax_cmin, 'min', 0, 1, valinit=c_min)
s_cmax = Slider(ax_cmax, 'max', 0, 1, valinit=c_max)
def update(val, s=None):
_cmin = s_cmin.val
_cmax = s_cmax.val
img.set_clim([_cmin, _cmax])
plt.draw()
s_cmin.on_changed(update)
s_cmax.on_changed(update)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
s_cmin.reset()
s_cmax.reset()
button.on_clicked(reset)
plt.show()
This is a minimally edited version of the official demo.