I have 4 columns in a dataframe which I would like to see whether there is a correlation. I thought it could give me some insight by ploting them in a 3D plot and then adding the 4th dimension as a heatmap, but I have no ideia how to add this heatmap linked to one column in a dataframe.
This is what I've got so far:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(12,12));
ax = fig.add_subplot(111, projection='3d');
ax.scatter(Series1,Series2,Series3);
Which returns me this:
(https://i.stack.imgur.com/qRGMv.png)
So, there are points of Series1,Series2 and Series3 on it... but is there a way to add a heatmap or anything to distinguish from a Series4?
I figured out that there is a way to add a colormap by adding the c parameter, as in:
withcolormap = ax.scatter(Series1, Series2, Series3, c = Series4, cmap='gist_heat');
And then by ploting its colobar aside:
fig.colorbar(withcolormap, shrink=0.75);
As it gave me any insights, I wonder how to plot an animation of a rotating view of this plot, I tried it by doing this code:
from matplotlib.animation import FuncAnimation, PillowWriter
def rotation(i):
ax = fig.add_subplot(111, projection='3d')
ax.view_init(elev=30, azim=i);
ani = FuncAnimation(withcolormap, rotation, frames=range(0,360,10))
And then it returns an error:
AttributeError: 'Path3DCollection' object has no attribute 'canvas'
Related
I'm pretty new to Python. I'm trying to plot a box plot for a sample data
I'm trying to plot box plots of mean value of the shared data. I got that part of the code. I'm also trying to plot standard error values on this box plot using yerr().
My code:
data3=pd.read_csv('demo1.csv')
names=['brow', 'harr', 'hage', 'buch', 'mcre']
d=[data3['brow'].mean(),data3['harr'].mean(),data3['hage'].mean(),data3['buch'].mean(),data3['mcre'].mean()]
N=len(data3['co'])
l=math.sqrt(N)
k=[(data3['brow'].std())/l,(data3['harr'].std())/l,(data3['hage'].std())/l,(data3['buch'].std())/l,(data3['mcre'].std())/l,(data3['phil'].std())/l,(data3['moor'].std())/l]
fig, ax = plt.subplots()
plt.bar(names,d)
plt.bar(len(names),d,yerr=k,align='center',alpha=0.5,ecolor='black',capsize=10)
Im getting an image such as this
But I want the black lines to be against each bar graph and not as a new bar in the plot with all of them together. How can I change this. Am I using the plt the wrong way? Please help.
I don't understand what you were trying to do with your second call to plt.bar()
import math
names=['brow', 'harr', 'hage', 'buch', 'mcre']
data3 = pd.DataFrame({n: np.random.normal(loc=np.random.randint(5,10), scale=np.random.randint(1,10), size=(100,)) for n in names})
d=data3[names].mean()
N=100
l=math.sqrt(N)
k=data3[names].std()/l
fig, ax = plt.subplots()
plt.bar(names,d,yerr=k,align='center',alpha=0.5,ecolor='black',capsize=10)
I tried to plot the subplots using the below code .But I am getting 'AttributeError: 'numpy.ndarray' object has no attribute 'boxplot'.
but changing the plt.subplots(1,2) it is plotting the box plot with indexerror.
import matplotlib.pyplot as plt
import seaborn as sns
fig = plt.Figure(figsize=(10,5))
x = [i for i in range(100)]
fig , axes = plt.subplots(2,2)
for i in range(4):
sns.boxplot(x, ax=axes[i])
plt.show();
I am expecting four subplots should be plotted but AttributeError is throwing
Couple of issues in your plot:
You are defining the figure twice which is not needed. I merged them into one.
You were looping 4 times using range(4) and using axes[i] for accessing the subplots. This is wrong for the following reason: Your axes is 2 dimensional so you need 2 indices to access it. Each dimension has length 2 because you have 2 rows and 2 columns so the only indices you can use are 0 and 1 along each axis. For ex. axes[0,1], axes[1,0] etc.
As #DavidG pointed out, you don't need the list comprehension. YOu can directly use range(100)
The solution is to expand/flatten make your 2d axes object and then directly iterate over it which gives you individual subplot, one at a time. The order of subplots will be row wise.
Complete working code
import matplotlib.pyplot as plt
import seaborn as sns
x = range(100)
fig , axes = plt.subplots(2,2, figsize=(10,5))
for ax_ in axes.flatten():
sns.boxplot(x, ax=ax_)
plt.show()
I'm just starting using Matplotlib the "right" way. I'm writing various programs that will each give me back a time series, and I'm looking to superimpose the graphs of the various time series, like this:
I think what I want is a single Axes instance defined in the main function, then I call each of my little functions, and they all return a Line2D instance, and then I'll put them all on the Axes object I created.
But I'm having trouble taking an existing Line2D object and adding it to an existing Axes object (like I'd want to do with the output of my function.) I thought of taking a Line2D called a and say ax.add_line(a).
import matplotlib.pyplot as plt
a, = plt.plot([1,2,3], [3,4,5], label = 'a')
fig, ax = plt.subplots()
ax.add_line(a)
Gives me a RuntimeError: "Can not put single artist in more than one figure."
I'm guessing that over time Matplotlib has stopped wanting users to be able to add a given line to any Axes they want. A similar thing is discussed in the comments of this answer, except there they're talking about an Axes object in two different Figure objects.
What's the best way to accomplish what I want? I'd rather keep my main script tidy, and not say ax.plot(some_data) over and over when I want to superimpose these lines.
Indeed, you cannot add the same artist to more than one axes or figure.
But for what I understand from your question, that isn't really necessary.
So let's just do as you propose;
"I thought of taking a Line2D called a and say ax.add_line(a)."
import numpy as np
import matplotlib.pyplot as plt
def get_line(label="a"):
return plt.Line2D(np.linspace(0,1,10), np.random.rand(10), label = label)
fig, ax = plt.subplots()
ax.add_line(get_line(label="a"))
ax.add_line(get_line(label="b"))
ax.add_line(get_line(label="z"))
ax.legend()
plt.show()
The way matplotlib would recommend is to create functions that take an axes as input and plot to that axes.
import numpy as np
import matplotlib.pyplot as plt
def plot_line(ax=None, label="a"):
ax = ax or plt.gca()
line, = ax.plot(np.linspace(0,1,10), np.random.rand(10), label = label)
return line
fig, ax = plt.subplots()
plot_line(ax, label="a")
plot_line(ax, label="b")
plot_line(ax, label="z")
ax.legend()
plt.show()
A possible work around for your problem:
import matplotlib.pyplot as plt
x = np.array([1,2,3])
y = np.array([3,4,5])
label = '1'
def plot(x,y,label):
a, = plt.plot(x,y, label = label)
return a
fig, ax = plt.subplots()
plot(x,y,label)
plot(x,1.5*y,label)
You can put your plot command now in a loop with changing labels. You can still use the ax handle to modify/define the plot parameters.
I cannot figure out how to make the legends not overlap with my figures (see below figure) in subplots. The problem is my axes are complicated because they are from a windrose. To get the axes:
1) I have downloaded the windrose.py from https://github.com/akrherz/windrose/tree/darylchanges
2) I copied the windrose.py into the same path with my python script, example.py
3) I changed windrose.py so that it is able to do subplots, according to the steps from Subplot of Windrose in matplotlib . Those steps were to make WindroseAxes as a projection into matplotlib. I edited the file windrose.py:
3a) Include an
import from matplotlib.projections import register_projection
at the beginning of the file.
3b) Then add a name variable :
class WindroseAxes(PolarAxes):
name = 'windrose'
...
3c) Finally, at the end of windrose.py, you add:
register_projection(WindroseAxes)
Once that is done, you can easily create your windrose axes using the projection argument to the matplotlib axes.
4) Now I ran my script below (example of my real script)
from windrose import WindroseAxes
import numpy as np
import matplotlib.pyplot as plt
from windrose_subplot import WindroseAxes
wind_speeds1 = np.array([12,10,13,15])
wind_dirs1 = np.array([60,76,32,80]) # in degrees
wind_speeds2 = np.array([23,12,10,8])
wind_dirs2 = np.array([23,45,29,13])
fig = plt.figure()
ax1 = fig.add_subplot(231,projection='windrose')
ax1.bar(wind_dirs1,wind_speeds1,normed=True,opening=0.8,edgecolor='white')
ax2 = fig.add_subplot(232,projection='windrose')
ax2.bar(wind_dirs2,wind_speeds2,normed=True,opening=0.8,edgecolor='white')
ax1.legend()
ax2.legend()
plt.tight_layout()
plt.show()
Ideally, I would like to create one legend with the max/min of all the subplots because they are all the same units . This legend will have to be the corresponding colors for each subplot for the same values across subplots (eg, a single normal legend relevant to all subplots). There will be 6 subplots in the real script but 2 here for now shows the point.
This is simple to fix. In order to only plot one legend, comment out or delete where you plot the first legend. In order to move the legend off of the plot, use bbox_to_anchor=() with some logical location. See below for an example that works for this example.
import numpy as np
import matplotlib.pyplot as plt
from windrose_subplot import WindroseAxes
wind_speeds1 = np.array([12,10,13,15])
wind_dirs1 = np.array([60,76,32,80]) # in degrees
wind_speeds2 = np.array([23,12,10,8])
wind_dirs2 = np.array([23,45,29,13])
fig = plt.figure()
ax1 = fig.add_subplot(231,projection='windrose')
ax1.bar(wind_dirs1,wind_speeds1,normed=True,opening=0.8,edgecolor='white')
ax2 = fig.add_subplot(232,projection='windrose')
ax2.bar(wind_dirs2,wind_speeds2,normed=True,opening=0.8,edgecolor='white')
# ax1.legend()
ax2.legend(bbox_to_anchor=(1.2 , -0.1))
plt.tight_layout()
plt.show()
However, note the bbox_to_anchor is reliant on the axis that the legend comes from, so
ax1.legend(bbox_to_anchor=1.2, -0.1))
#ax2.legend()
would display the legend underneath the second axis:
Thank you Hazard11, I found your answer very useful :) There is an issue with the answer though is the legend does not represent the first subplot because the bins are generated when creating the second subplot.
I just solved this issue by calculating the bins using numpy.histogram first and then passing that to windrose.WindroseAxes.bar() when creating each wind rose. Doing it this way means you need to pick which one you want to use to generate the bins. Another way to do it would be to define the bins manually or to create a function which generates some efficient binning for both which could then be used.
wind_speeds1 = np.array([12,10,13,15])
wind_dirs1 = np.array([60,76,32,80]) # in degrees
wind_speeds2 = np.array([23,12,10,8])
wind_dirs2 = np.array([23,45,29,13])
wind_speeds_bins = np.histogram(wind_speeds2, 5)[1]
fig = plt.figure()
ax1 = fig.add_subplot(231, projection='windrose')
ax1.bar(wind_dirs1 ,wind_speeds1, normed=True, opening=0.8, edgecolor='white', bins=wind_speeds_bins)
ax2 = fig.add_subplot(232, projection='windrose')
ax2.bar(wind_dirs2, wind_speeds2, normed=True, opening=0.8, edgecolor='white', bins=wind_speeds_bins)
# ax1.legend()
ax2.legend(bbox_to_anchor=(1.2 , -0.1))
plt.tight_layout()
plt.show()
I'm plotting a figure and hope to set the figure shape and tick positions. But I find that I cannot do the two things together. For example, if I use the following code:
import matplotlib
import matplotlib.pyplot as plt
ls = range(0,10)
fig, ax = plt.subplots()
# set figure shape
plt.figure(figsize=(10,5))
plt.ylim([0,10])
plt.plot(ls)
figname = 'aaa.jpg'
# set ytick positions
ax.set_yticks([1,3,5,7,9])
plt.savefig(figname,format='jpg')
Then I get the following figure.
The shape is correct. But the ytick is not changed by the code line ax.set_yticks([1,3,5,7,9]).
Then I try the following code (i.e. move the sentence plt.figure(figsize=(10,5)) to the beginning of the program):
import matplotlib
import matplotlib.pyplot as plt
# set figure shape
plt.figure(figsize=(10,5))
ls = range(0,10)
fig, ax = plt.subplots()
plt.ylim([0,10])
plt.plot(ls)
figname = 'aaa.jpg'
# set ytick position
ax.set_yticks([1,3,5,7,9])
plt.savefig(figname,format='jpg')
Then I get the following figure:
The ytick is correct. Yicks appear in positions [1,3,5,7,9]. However, the figure shape is not the shape I set.
How to do the two things together?
Thank you all for helping me!!!
you can set the figsize in the subplot function instead.
what went wrong in the first graph:
plt.figure(figsize=(10,5))
the above line of code is creating a new figure on which your graph is being plotted, the 'ax' on which you are setting the y ticks is related to a subplot which is different.
ls = range(0,10)
fig, ax = plt.subplots(figsize=(10,5))
ax.set_yticks([1,3,5,7,9])
ax.set_xticks([1,3,5,7,9])
plt.grid()
plt.plot(ls)
The plot is showing exactly what you're trying to do