I am currently making a plot on matplotlib, which looks like below.
The code for which is:
fig, ax1 = plt.subplots(figsize=(20,5))
ax2 = ax1.twinx()
# plt.subplots_adjust(top=1.4)
ax2.fill_between(dryhydro_df['Time'],dryhydro_df['Flow [m³/s]'],0,facecolor='lightgrey')
ax2.set_ylim([0,10])
AB = ax2.fill_between(dryhydro_df['Time'],[12]*len(dryhydro_df['Time']),9.25,facecolor=colors[0],alpha=0.5,clip_on=False)
ab = ax2.scatter(presence_df['Datetime'][presence_df['AB']==True],[9.5]*sum(presence_df['AB']==True),marker='X',color='black')
# tidal heights
ax1.plot(tide_df['Time'],tide_df['Tide'],color='dimgrey')
I want the blue shaded region and black scatter to be above the plot. I can move the elements above the plot by using clip_on=False but I think I need to extend the space above the plot to do visualise it. Is there a way to do this? Mock-up of what I need is below:
You can use clip_on=False to draw outside the main plot. To position the elements, an xaxis transform helps. That way, x-values can be used in the x direction, while the y-direction uses "axes coordinates". ax.transAxes() uses "axes coordinates" for both directions.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
dates = pd.date_range('2018-07-01', '2018-07-31', freq='H')
xs = dates.to_numpy().astype(float)
ys = np.sin(xs * .091) * (np.sin(xs * .023) ** 2 + 1)
fig, ax1 = plt.subplots(figsize=(20, 5))
ax1.plot(dates, ys)
ax1.scatter(np.random.choice(dates, 10), np.repeat(1.05, 10), s=20, marker='*', transform=ax1.get_xaxis_transform(),
clip_on=False)
ax1.plot([0, 1], [1.05, 1.05], color='steelblue', lw=20, alpha=0.2, transform=ax1.transAxes, clip_on=False)
plt.tight_layout() # fit labels etc. nicely
plt.subplots_adjust(top=0.9) # make room for the additional elements
plt.show()
Related
I would like to reproduce this plot in Python: (https://i.stack.imgur.com/6CRfn.png)
Any idea how to do this?
I tried to do a normal plt.scatter() but I can't draw this axes on the zero, for example.
That's a very general question... Using plt.scatter() is certainly a good option. Then just add the two lines to the plot (e.g. using axhline and axvline).
Slightly adapting this example:
import numpy as np
import matplotlib.pyplot as plt
# don't show right and top axis[![enter image description here][1]][1]
import matplotlib as mpl
mpl.rcParams['axes.spines.right'] = False
mpl.rcParams['axes.spines.top'] = False
# some random data
N = 50
x = np.random.randint(-10, high=11, size=N, dtype=int)
y = np.random.randint(-10, high=11, size=N, dtype=int)
colors = np.random.rand(N)
area = (30 * np.random.rand(N))**2 # 0 to 15 point radii
# creating a vertical and a horizontal line
plt.axvline(x=0, color='grey', alpha=0.75, linestyle='-')
plt.axhline(y=0, color='grey', alpha=0.75, linestyle='-')
# scatter plot
plt.scatter(x, y, s=area, c=colors, alpha=0.5)
plt.show()
I have some datasets that I'm visualizing in a scatter plot. I have a bunch of mean values, and a global mean. What I'm after, but cant really achieve,is to have a scatter plot that is centered in the plot, while also placing the origin at the global mean.
This is the code that defines the layout of the plot:
plt.figure(1)
plt.suptitle('Example')
plt.xlabel('x (pixels)')
plt.ylabel('y (pixels)')
ax = plt.gca()
ax.spines['left'].set_position('center')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_color('none')
ax.scatter(x_data, y_data, color=color, alpha=0.08, label=csv_file_name)
ax.plot(global_mean[0], global_mean[1], color='green',
marker='x', label='Global mean')
This produces the following plot (the ax.scatter() is called multiple times for each dataset, but it's not in the code above):
I've tried playing around with the ax.set_position() parameters but nothing have worked well so far. Is there a way to do what I'm after with matplotlib, or do I need to use some other plot library?
You can use the ax.spines() method to move them around.
import numpy as np
import random
import matplotlib.pyplot as plt
#generate some random data
x = np.linspace(1,2, 100)
y = [random.random() for _ in range(100)]
fig = plt.figure(figsize=(10,5))
# original plot
ax = fig.add_subplot(1,2,1)
ax.scatter(x, y)
# same plot, but with the spines moved
ax2 = fig.add_subplot(1,2,2)
ax2.scatter(x, y)
# move the left spine (y axis) to the right
ax2.spines['left'].set_position(('axes', 0.5))
# move the bottom spine (x axis) up
ax2.spines['bottom'].set_position(('axes', 0.5))
# turn off the right and top spines
ax2.spines['right'].set_visible(False)
ax2.spines['top'].set_visible(False)
plt.show()
I need to achieve the following effect using matplotlib:
As you can see it's a combination of plots in different quadrants.
I do know how to generate each quadrant individually. For example, for the 'x invert' quadrant's plot I would simply use:
plt.plot(x, y)
plt.gca().invert_yaxis()
plt.show()
to draw the plot. It properly inverts the x axis. However, it would only generate top-left quadrant's plot for me.
How can I generate a combination of plots described in the above picture? Each quadrant has its own plot with different inverted axises.
My best idea was to merge it in some tool like Paint.
I don't have enough reputation to add a comment to add on to ImportanceOfBeingErnest's comment, but when you create the 4 subplots you'll want to remove the space between the plots as well as have shared axes (and clean up overlapping ticks).
There are various ways to do subplots, but I prefer gridspec. You can create a 2x2 grid with gridspec and do all of this, here's an example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure()
# lines to plot
x = np.arange(0, 10)
y = np.arange(0, 10)
# gridspec for 2 rows, 2 cols with no space between
grid = gridspec.GridSpec(nrows=2, ncols=2, hspace=0, wspace=0, figure=fig)
x_y = fig.add_subplot(grid[0, 1], zorder=3)
x_y.plot(x, y)
x_y.margins(0)
invx_y = fig.add_subplot(grid[0, 0], zorder=2, sharey=x_y)
invx_y.plot(-x, y)
invx_y.margins(0)
invx_invy = fig.add_subplot(grid[1, 0], zorder=0, sharex=invx_y)
invx_invy.plot(-x, -y)
invx_invy.margins(0)
x_invy = fig.add_subplot(grid[1, 1], zorder=1, sharey=invx_invy, sharex=x_y)
x_invy.plot(x, -y)
x_invy.margins(0)
# clean up overlapping ticks
invx_y.tick_params(labelleft=False, length=0)
invx_invy.tick_params(labelleft=False, labelbottom=False, length=0)
x_invy.tick_params(labelbottom=False, length=0)
x_y.set_xticks(x_y.get_xticks()[1:-1])
invx_y.set_xticks(invx_y.get_xticks()[1:-1])
x_invy.set_yticks(x_invy.get_yticks()[1:-1])
plt.show()
This yields the following figure:
Is it possible to color axis spine with multiple colors using matplotlib in python?
Desired output style:
You can use a LineCollection to create a multicolored line. You can then use the xaxis-transform to keep it fixed to the xaxis, independent of the y-limits. Setting the actual spine invisible and turning clip_on off makes the LineCollection look like the axis spine.
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np
fig, ax = plt.subplots()
colors=["b","r","lightgreen","gold"]
x=[0,.25,.5,.75,1]
y=[0,0,0,0,0]
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments,colors=colors, linewidth=2,
transform=ax.get_xaxis_transform(), clip_on=False )
ax.add_collection(lc)
ax.spines["bottom"].set_visible(False)
ax.set_xticks(x)
plt.show()
Here is a slightly different solution. If you don't want to recolor the complete axis, you can use zorder to make sure the colored line segments are visible on top of the original axis.
After drawing the main plot:
save the x and y limits
draw a horizontal line at ylims[0] between the chosen x-values with the desired color
clipping should be switched off to allow the line to be visible outside the strict plot area
zorder should be high enough to put the new line in front of the axes
the saved x and y limits need to be put back, because drawing extra lines moved them (alternatively, you might have turned off autoscaling the axes limits by calling plt.autoscale(False) before drawing the colored axes)
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(0, 20, 100)
for i in range(10):
plt.plot(x, np.sin(x*(1-i/50)), c=plt.cm.plasma(i/12))
xlims = plt.xlim()
ylims = plt.ylim()
plt.hlines(ylims[0], 0, 10, color='limegreen', lw=1, zorder=4, clip_on=False)
plt.hlines(ylims[0], 10, 20, color='crimson', lw=1, zorder=4, clip_on=False)
plt.vlines(xlims[0], -1, 0, color='limegreen', lw=1, zorder=4, clip_on=False)
plt.vlines(xlims[0], 0, 1, color='crimson', lw=1, zorder=4, clip_on=False)
plt.xlim(xlims)
plt.ylim(ylims)
plt.show()
To highlight an area on the x-axis, also axvline or axvspan can be interesting. An example:
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(0, 25, 100)
for i in range(10):
plt.plot(x, np.sin(x)*(1-i/20), c=plt.cm.plasma(i/12))
plt.axvspan(10, 20, color='paleturquoise', alpha=0.5)
plt.show()
I am trying to get something like this (image the image is fully populated with the red squares (I only drew a few)): . Expanding on what I want: I want the RED squares to be centred int the YELLOW squares as shown in the picture (but with RED squares in ALL the YELLOW squares).
What is happening there is the bigger windows (yellow grid) are overlapped between each other by half their size, where the smaller windows in this case, half the size of the big window, (red square) are centred on the centre of the big window. The furthest I could get was using this Multiple grids on matplotlib I am basically using their code, but to make things absolutely clear, I include the code:
EDIT: Thanks to Rutgers I got what I wanted. Here is a slightly edited and shortened version. This code gives the first centre of the four yellow grid intersection where I want.
import matplotlib.pyplot as plt
from matplotlib.pyplot import subplot
from scipy.misc import imread
import numpy as np
import matplotlib.cm as cmps
import matplotlib.collections as collections
i = 1
initial_frame = 1
ax = subplot(111)
bg = imread("./png/frame_" + str("%05d" % (i + initial_frame) ) + ".png").astype(np.float64)
# define the normal (yellow) grid
ytcks = np.arange(16,bg.shape[0],32)
xtcks = np.arange(16,bg.shape[1],32)
# plot the sample data
ax.imshow(bg, cmap=plt.cm.Greys_r, interpolation='none')
ax.set_xticks(xtcks)
ax.set_xticks(xtcks+16, minor=True)
ax.set_yticks(ytcks)
ax.set_yticks(ytcks+16, minor=True)
ax.xaxis.grid(True,'minor', linestyle='--', lw=1., color='y')
ax.yaxis.grid(True,'minor', linestyle='--', lw=1., color='y')
ax.xaxis.grid(True,'major', linestyle='--', lw=0.5, color='g')
ax.yaxis.grid(True,'major', linestyle='--', lw=0.5, color='g')
plt.show()
Given the sample data z from my other answer:
# define the normal (yellow) grid
tcks = np.arange(0,90,10)
fig, ax = plt.subplots(figsize=(8,8))
# plot the sample data
ax.imshow(z, cmap=plt.cm.Greys_r, interpolation='none', vmin=0.4, vmax=1.5, extent=[0,z.shape[0],0,z.shape[1]])
ax.set_xticks(tcks)
ax.set_xticks(tcks+5, minor=True)
ax.set_yticks(tcks)
ax.set_yticks(tcks+5, minor=True)
ax.xaxis.grid(True,'minor', linestyle='--', lw=1., color='y')
ax.yaxis.grid(True,'minor', linestyle='--', lw=1., color='y')
ax.xaxis.grid(True,'major', linestyle='-', lw=1., color='r')
ax.yaxis.grid(True,'major', linestyle='-', lw=1., color='r')
ax.set_xlim(0,80)
ax.set_ylim(0,80)
I expect this to be much faster than drawing with polygons.
Mentioning bigger and smaller grid is a little bit confusing, since to me they seem of equal size, but i assume you mean the 'major' and 'minor' grid.
Well, to sort of mimic your picture with what i had in mind, see if this makes any sense:
import matplotlib.collections as collections
import numpy as np
import matplotlib.pyplot as plt
# generate some fake data, after:
# http://matplotlib.org/examples/images_contours_and_fields/pcolormesh_levels.html
dx, dy = 0.05, 0.05
y, x = np.mgrid[slice(1, 5, dy), slice(1, 5, dx)]
z = np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
# define the normal (yellow) grid
tcks = np.arange(0,90,10)
fig, ax = plt.subplots(figsize=(8,8), subplot_kw={'xticks': tcks, 'yticks': tcks})
# plot the sample data
ax.imshow(z, cmap=plt.cm.Greys_r, interpolation='none', vmin=0.4, vmax=1.5, extent=[0,z.shape[0],0,z.shape[1]])
# plot the yellow grid
ax.grid(True, linestyle='--', color='y', lw=1.5, alpha=1.0)
# define some random 'red' grid cells
custom_grid = []
for i in range(10):
x = np.random.randint(0,7) * 10 + 5
y = np.random.randint(0,7) * 10 + 5
polygon = plt.Rectangle((x, y), 10, 10)
custom_grid.append(polygon)
p = collections.PatchCollection(custom_grid, facecolor='none', edgecolor='r', lw=1.5)
ax.add_collection(p)
Its stil a bit unclear for example when you want to show the 'red' grid cells and when not.