Python, changing axis of 3D plot - python

I have the following code:
xx = np.arange(len(days[0]))
ys = [i+xx+(i*xx)**2 for i in range(len(days[0]))]
colors = cm.rainbow(np.linspace(0, 1, len(ys)))
for d,cc in zip(days[0],colors):
ax.scatter(t,p,d,color=cc)
t and p are lists (time and price) and d is an integer (day). When I run the code the result I get is below :
The issue is that the axis are wrong. p and d need to be swapped but
when I try to do:
ax.scatter(t,d,p)
I get an error saying "Arguments xs and ys must be of same size". Is there any way I can just get the axis to be switched since intuitively the plot does not make sense in this configuration.
The reason that the days are iterated over is so that I can have a separate color for each day on the plot.
I tried the solution of iterating through the t and p lists for each day and just plotting individual corresponding t,d,p points, However that is much slower and afterwards the matplotlib plot is unresponsive if you try to move it.

I'm not sure why you are getting an error message, but can you give a sample of your data? The following code works fine, and produces the type of plot you are asking for.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
# Generate some dummy data
time = np.random.rand(100)
price = 120+10*np.random.rand(100)
day = np.random.randint(0,10,100)
# Plot data
fig = plt.figure(figsize=(12,4))
ax = fig.add_subplot(121, projection='3d')
ax.scatter(time, price, day)
ax.set_xlabel('time')
ax.set_ylabel('price')
ax.set_zlabel('day')
ax = fig.add_subplot(122, projection='3d')
ax.scatter(time, day, price)
ax.set_xlabel('time')
ax.set_ylabel('day')
ax.set_zlabel('price')
fig.show()
Edit:
You can set the colour of the points in a scatter plot by passing a list/array. If we plot the second scatter plot using:
ax.scatter(time, day, price, c=day)
We get:

Related

Set margins of a time series plotted with pandas

I have the following code for generating a time series plot
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
series = pd.Series([np.sin(ii*np.pi) for ii in range(30)],
index=pd.date_range(start='2019-01-01', end='2019-12-31',
periods=30))
series.plot(ax=ax)
I want to set an automatic limit for x and y, I tried using ax.margins() but it does not seem to work:
ax.margins(y=0.1, x=0.05)
# even with
# ax.margins(y=0.1, x=5)
What I am looking for is an automatic method like padding=0.1 (10% of whitespace around the graph)
Pandas and matplotlib seem to be confused rather often while collaborating when axes have dates. For some reason in this case ax.margins doesn't work as expected with the x-axis.
Here is a workaround which does seem to do the job, explicitely moving the xlims:
xmargins = 0.05
ymargins = 0.1
ax.margins(y=ymargins)
x0, x1 = plt.xlim()
plt.xlim(x0-xmargins*(x1-x0), x1+xmargins*(x1-x0))
Alternatively, you could work directly with matplotlib's plot, which does work as expected applying the margins to the date axis.
ax.plot(series.index, series)
ax.margins(y=0.1, x=0.05)
PS: This post talks about setting use_sticky_edges to False and calling autoscale_view after setting the margins, but also that doesn't seem to work here.
ax.use_sticky_edges = False
ax.autoscale_view(scaley=True, scalex=True)
You can use ax.set_xlim and ax.set_ylim to set the x and y limits of your plot respectively.
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
series = pd.Series([np.sin(ii*np.pi) for ii in range(30)],
index=pd.date_range(start='2019-01-01', end='2019-12-31',
periods=30))
# set xlim to be a between certain dates
ax.set_xlim((pd.to_datetime('2019-01-01'), pd.to_datetime('2019-01-31'))
# set ylim to be between certain values
ax.set_ylim((-0.5, 0.5))
series.plot(ax=ax)

x-axis labelling with matplotlib

I have a two dimensional (numpy)array and I plot the first column with the command plt.plot(wp[:, 0]). This shows exactly what I want and there is nothing I want to change besides the x axis labelling. For the x axis I am searching for a command which shows the area where the the value of the second column is the same and also which displays the y-number of this area.
[x1,y1]
[x2,y2]
[x3,y2]
[x4,y3]
[x5,y3]
[x6,y3]
[x7,y4]
As u can the see in my example matrix, the entries in the second column are not unique but instead there are "regions" with the same value.
Edit: So plt.xticks(tx, wp[:,2], rotation='vertical')does work for smaller matrices but looks really ugly for larger ones:
So in my opinion it would be enough if each number would just occur once. Do you know how to do that?
You'll have to:
Customize the number of ticks
Customize what to print when for a certain value
Modified from the examples:
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter, MaxNLocator
fig = plt.figure()
ax = fig.add_subplot(111)
xs = range(100)
ys = range(100)
def format_fn(tick_val, tick_pos):
return '{0}'.format(int(tick_val))[:1]
ax.xaxis.set_major_formatter(FuncFormatter(format_fn))
ax.xaxis.set_major_locator(MaxNLocator(nbins=6,integer=True))
ax.plot(xs, ys)
plt.show()

Matplotlib: multiple 3D lines all get drawn using the final y-value in my loop

I am trying to plot multiple lines in a 3D figure. Each line represents a month: I want them displayed parallel in the y-direction.
My plan was to loop over a set of Y values, but I cannot make this work properly, as using the ax.plot command (see working code below) produces a dozen lines all at the position of the final Y value. Confusingly, swapping ax.plot for ax.scatter does produce a set of parallel lines of data (albeit in the form of a set of dots; ax.view_init set to best display the parallel aspect of the result).
How can I use a produce a plot with multiple parallel lines?
My current workaround is to replace the loop with a dozen different arrays of Y values, and that can't be the right answer.
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
# preamble
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
cs = ['r','g','b','y','r','g','b','y','r','g','b','y']
# x axis
X = np.arange(24)
# y axis
y = np.array([15,45,75,105,135,165,195,225,255,285,315,345])
Y = np.zeros(24)
# data - plotted against z axis
Z = np.random.rand(24)
# populate figure
for step in range(0,12):
Y[:] = y[step]
# ax.plot(X,Y,Z, color=cs[step])
ax.scatter(X,Y,Z, color=cs[step])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# set initial view of plot
ax.view_init(elev=80., azim=345.)
plt.show()
I'm still learning python, so simple solutions (or, preferably, those with copious explanatory comments) are greatly appreciated.
Use
ax.plot(X, np.array(Y), Z, color=cs[step])
or
Y = [y[step]] * 24
This looks like a bug in mpl where we are not copying data when you hand it in so each line is sharing the same np.array object so when you update it all of your lines.

Matplotlib Subplot Datetime X-Axis Ticks Not Working As Intended

I'm attempting to plot many plots, here's a sample of how the data is organized:
My intention is to build a series of subplots for either hours or days (say 7 days in a week, or 24 hours in a day) using google analytics data. My index are date-time objects.
Here's an example of how a single plot looks, when the axis is done correctly.
from datetime import datetime, date, timedelta
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import matplotlib.dates as dates
#creating our graph and declaring our locator/formatters used in axis labelling.
hours = dates.HourLocator(interval=2)
hours_ = dates.DateFormatter('%I %p')
el = datetime(year=2016, day=1, month=3, hour=0)
fig, ax = plt.subplots(ncols = 1, nrows= 1)
fig.set_size_inches(18.5, 10.5)
fig.tight_layout()
ax.set_title(el.strftime('%a, %m/%d/%y'))
ax.plot(df_total.loc[el:el+timedelta(hours=23, minutes=59),:].index,
df_total.loc[el:el+timedelta(hours=23, minutes=59),:].hits, '-')
ax.xaxis.set_major_locator(hours)
ax.xaxis.set_major_formatter(hours_)
fig.show()
As you can see, the x axis looks good, working as intended with the right ticks/date labels.
However, when I try and run the same plot on a subplot series, I'm running into the following error. Here's my code:
fig, ax = plt.subplots(ncols = 3, nrows= 2)
fig.set_size_inches(18.5, 10.5)
fig.tight_layout()
nrows=2
ncols=3
count = 0
for row in range(nrows):
for column in range(ncols):
el = cleaned_date_range[count]
ax[row][column].set_title(el.strftime('%a, %m/%d/%y'))
ax[row][column].xaxis.set_major_locator(hours)
ax[row][column].xaxis.set_major_formatter(hours_)
ax[row][column].plot(df_total.loc[el:el+timedelta(hours=23,minutes=59),:].index, df_total.loc[el:el+timedelta(hours=23,minutes=59),:].hits)
count += 1
if count == 7:
break
However, that yields the very funky plot below, with mislabelled axes:
I experimented with adding an additional row to see if it was just covering up because of vertical space:
but was confronted with the same behavior, only the last subplot's axes appears to be working with the rest not working.
Any insight would be appreciated!
so the answer is in the following github issue raised a few years ago related to the set_major_locator() and set_major_formatter() objects:
https://github.com/matplotlib/matplotlib/issues/1086/
to quote eric:
"You are missing something, but it is something that is quite non-intuitive and easy to miss: Locators can't be shared among axes. The set_major_locator() method assigns its axis to that Locator, overwriting any axis that was previously assigned."
so the solution is to instantiate a new dates.MinuteLocator and dates.DateFormatter object for each new axes, e.g:
for ax in list_of_axes:
minutes = dates.MinuteLocator(interval=5)
minutes_ = dates.DateFormatter('%I:%M %p')
ax.xaxis.set_major_locator(minutes)
ax.xaxis.set_major_formatter(minutes_)
I've experimented and it looks like you don't need to reference the dates.Locator and dates.Formatter objects after the plot so it's ok to just re-instantiate with each loop using the same name. (I could be wrong here though!)
I had the same missing subplot datetime x-axis tick marks issue. The following code, which is quite similar to the OP's, seems to work, see the attached figure. However, I'm using matplotlib 3.1.0, perhaps the issue has been addressed in this version? But I do have one observation: if I enable fig.autofmt_xdate() for the second subplot, the first subplot datetime x-axis will not display.
fig = plt.figure()
plt.rcParams['figure.figsize'] = (width, height)
plt.subplots_adjust(wspace=0.25, hspace=0.2)
ax = fig.add_subplot(2,1,1)
ax.xaxis.set_major_locator(MonthLocator(bymonthday=1))
ax.xaxis.set_major_formatter(DateFormatter('%Y-%b'))
ax.plot(df1['DATE'], df1['Movement'], '-')
plt.ylabel(r'$D$', fontsize=18)
plt.xticks(fontsize=12)
plt.yticks(fontsize=16)
plt.legend(fontsize=16, frameon=False)
fig.autofmt_xdate()
ax = fig.add_subplot(2,1,2)
ax.xaxis.set_major_locator(MonthLocator(bymonthday=1))
ax.xaxis.set_major_formatter(DateFormatter('%Y-%b'))
ax.plot(df2['DATE'], df2['Movement'], '-')
#plt.ylabel(r'$D`enter code here`$', fontsize=18)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.legend(fontsize=16, frameon=False)
#fig.autofmt_xdate()
plt.show()

matplotlib: Creating two (stacked) subplots with SHARED X axis but SEPARATE Y axis values

I am using matplotlib 1.2.x and Python 2.6.5 on Ubuntu 10.0.4. I am trying to create a SINGLE plot that consists of a top plot and a bottom plot.
The X axis is the date of the time series. The top plot contains a candlestick plot of the data, and the bottom plot should consist of a bar type plot - with its own Y axis (also on the left - same as the top plot). These two plots should NOT OVERLAP.
Here is a snippet of what I have done so far.
datafile = r'/var/tmp/trz12.csv'
r = mlab.csv2rec(datafile, delimiter=',', names=('dt', 'op', 'hi', 'lo', 'cl', 'vol', 'oi'))
mask = (r["dt"] >= datetime.date(startdate)) & (r["dt"] <= datetime.date(enddate))
selected = r[mask]
plotdata = zip(date2num(selected['dt']), selected['op'], selected['cl'], selected['hi'], selected['lo'], selected['vol'], selected['oi'])
# Setup charting
mondays = WeekdayLocator(MONDAY) # major ticks on the mondays
alldays = DayLocator() # minor ticks on the days
weekFormatter = DateFormatter('%b %d') # Eg, Jan 12
dayFormatter = DateFormatter('%d') # Eg, 12
monthFormatter = DateFormatter('%b %y')
# every Nth month
months = MonthLocator(range(1,13), bymonthday=1, interval=1)
fig = pylab.figure()
fig.subplots_adjust(bottom=0.1)
ax = fig.add_subplot(111)
ax.xaxis.set_major_locator(months)#mondays
ax.xaxis.set_major_formatter(monthFormatter) #weekFormatter
ax.format_xdata = mdates.DateFormatter('%Y-%m-%d')
ax.format_ydata = price
ax.grid(True)
candlestick(ax, plotdata, width=0.5, colorup='g', colordown='r', alpha=0.85)
ax.xaxis_date()
ax.autoscale_view()
pylab.setp( pylab.gca().get_xticklabels(), rotation=45, horizontalalignment='right')
# Add volume data
# Note: the code below OVERWRITES the bottom part of the first plot
# it should be plotted UNDERNEATH the first plot - but somehow, that's not happening
fig.subplots_adjust(hspace=0.15)
ay = fig.add_subplot(212)
volumes = [ x[-2] for x in plotdata]
ay.bar(range(len(plotdata)), volumes, 0.05)
pylab.show()
I have managed to display the two plots using the code above, however, there are two problems with the bottom plot:
It COMPLETELY OVERWRITES the bottom part of the first (top) plot - almost as though the second plot was drawing on the same 'canvas' as the first plot - I can't see where/why that is happening.
It OVERWRITES the existing X axis with its own indice, the X axis values (dates) should be SHARED between the two plots.
What am I doing wrong in my code?. Can someone spot what is causing the 2nd (bottom) plot to overwrite the first (top) plot - and how can I fix this?
Here is a screenshot of the plot created by the code above:
[[Edit]]
After modifying the code as suggested by hwlau, this is the new plot. It is better than the first in that the two plots are separate, however the following issues remain:
The X axis should be SHARED by the two plots (i.e. the X axis should be shown only for the 2nd [bottom] plot)
The Y values for the 2nd plot seem to be formmated incorrectly
I think these issues should be quite easy to resolve however, my matplotlib fu is not great at the moment, as I have only recently started programming with matplotlib. any help will be much appreciated.
There seem to be a couple of problems with your code:
If you were using figure.add_subplots with the full
signature of subplot(nrows, ncols, plotNum) it may have
been more apparent that your first plot asking for 1 row
and 1 column and the second plot was asking for 2 rows and
1 column. Hence your first plot is filling the whole figure.
Rather than fig.add_subplot(111) followed by fig.add_subplot(212)
use fig.add_subplot(211) followed by fig.add_subplot(212).
Sharing an axis should be done in the add_subplot command using sharex=first_axis_instance
I have put together an example which you should be able to run:
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
import datetime as dt
n_pts = 10
dates = [dt.datetime.now() + dt.timedelta(days=i) for i in range(n_pts)]
ax1 = plt.subplot(2, 1, 1)
ax1.plot(dates, range(10))
ax2 = plt.subplot(2, 1, 2, sharex=ax1)
ax2.bar(dates, range(10, 20))
# Now format the x axis. This *MUST* be done after all sharex commands are run.
# put no more than 10 ticks on the date axis.
ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))
# format the date in our own way.
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
# rotate the labels on both date axes
for label in ax1.xaxis.get_ticklabels():
label.set_rotation(30)
for label in ax2.xaxis.get_ticklabels():
label.set_rotation(30)
# tweak the subplot spacing to fit the rotated labels correctly
plt.subplots_adjust(hspace=0.35, bottom=0.125)
plt.show()
Hope that helps.
You should change this line:
ax = fig.add_subplot(111)
to
ax = fig.add_subplot(211)
The original command means that there is one row and one column so it occupies the whole graph. So your second graph fig.add_subplot(212) cover the lower part of the first graph.
Edit
If you dont want the gap between two plots, use subplots_adjust() to change the size of the subplots margin.
The example from #Pelson, simplified.
import matplotlib.pyplot as plt
import datetime as dt
#Two subplots that share one x axis
fig,ax=plt.subplots(2,sharex=True)
#plot data
n_pts = 10
dates = [dt.datetime.now() + dt.timedelta(days=i) for i in range(n_pts)]
ax[0].bar(dates, range(10, 20))
ax[1].plot(dates, range(10))
#rotate and format the dates on the x axis
fig.autofmt_xdate()
The subplots sharing an x-axis are created in one line, which is convenient when you want more than two subplots:
fig, ax = plt.subplots(number_of_subplots, sharex=True)
To format the date correctly on the x axis, we can simply use fig.autofmt_xdate()
For additional informations, see shared axis demo and date demo from the pylab examples.
This example ran on Python3, matplotlib 1.5.1

Categories