I'm facing a problem where I have 13 data points but Matplotlib only plot until around 8-9 data points. Below is my code:
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
import numpy as np
pv = [1240, 1390, 1635, 1885, 2025, 2165, 2290, 2965, 3455, 4510, 4975, 5510, 5795]
ac = [1200, 1325, 1555, 1835, 1985, 2135, 2285, 2435, 2985]
ev = [1240, 1390, 1635, 1885, 2025, 2165, 2165, 2165, 2690]
time = np.arange(1, 14)
y1 = np.zeros(13)
fig = plt.figure(figsize=(10,5))
ax = plt.axes(projection='3d')
ax.plot(xs=time, ys=y1, zs=pv, label="PV")
ax.plot(xs=time[0: 9], ys=(y1+1)[0: 9], zs=ac, label="AC")
ax.plot(xs=time[0: 9], ys=(y1+2)[0: 9], zs=ev, label="EV")
ax.set_yticklabels([])
ax.set_xticks(time)
ax.set_title("Earned Value Analysis")
ax.set_xlabel("Time(Week)")
ax.set_zlabel("Cummulative Cost")
plt.legend(loc="lower right")
plt.show()
Here is the output of my program:
I should be getting around 5795 for PV when at time 13. However, the graph is only plotted until around time 8-9.
Thank you for anyone clearing my doubt.
Related
I have this plot in which I can adapt the curve as I want. My problem is I need to draw on an image. I donĀ“t know how to put both together.
1
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
#theta = np.arange(0, 2*np.pi, 0.1)
#r = 1.5
#xs = r*np.cos(theta)
#ys = r*np.sin(theta)
xs = (921, 951, 993, 1035, 1065, 1045, 993, 945)
ys = (1181, 1230, 1243, 1230, 1181, 1130, 1130, 1130)
poly = Polygon(list(zip(xs, ys)), animated=True)
fig, ax = plt.subplots()
ax.add_patch(poly)
p = PolygonInteractor(ax, poly, visible=False)
ax.set_title('Click and drag a point to move it')
ax.set_xlim((800, 1300))
ax.set_ylim((1000, 1300))
plt.show()
Try call ax.imshow before draw the polygon? Like this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from scipy import misc
xs = (21, 51, 93, 135, 100, 90, 21, 10)
ys = (111, 130, 143, 230, 501, 530, 530, 513)
poly = Polygon(list(zip(xs, ys)), color='r')
fig, ax = plt.subplots()
ax.imshow(misc.face(), origin='lower')
ax.add_patch(poly)
# ax.set_xlim([0,2000])
# ax.set_ylim([0,2000])
fig.show()
BTW, your xlim and ylim is also not proper. Your image is in the range of y=0~700, but your polygon is y=1000~1300. You at least need to ax.set_ylim([0,1400]) for your image and polygon shown together.
I'm trying to add a legend to my seaborn bar + line chart, but only getting the error "No handles with labels found to put in legend." whatever I try. How to go about this?
from pathlib import Path
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
import matplotlib.dates as mdates
import numpy as np
dfGroup = pd.DataFrame({
'Year': [1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920],
'Total Deaths': [0, 0, 2, 3, 2, 3, 4, 5, 6, 7, 8],
'Total Affected': [0, 1, 0, 2, 3, 6, 9, 8, 12, 13, 15]
})
# Add 3-year rolling average
dfGroup['rolling_3years'] = dfGroup['Total Deaths'].rolling(3).mean().shift(0)
dfGroup = dfGroup.fillna(0)
# Make a smooth line from the 3-year rolling average
from scipy.interpolate import make_interp_spline
X_Y_Spline = make_interp_spline(dfGroup['Year'], dfGroup['rolling_3years'])
# Returns evenly spaced numbers over a specified interval.
X_ = np.linspace(dfGroup['Year'].min(), dfGroup['Year'].max(), 500)
Y_ = X_Y_Spline(X_)
# Plot the data
a4_dims = (15, 10)
fig, ax1 = plt.subplots(figsize=a4_dims)
ax1 = sns.barplot(x = "Year", y = "Total Deaths",
data = dfGroup, color='#42b7bd')
ax2 = ax1.twinx()
ax2 = sns.lineplot(X_, Y_, marker='o')
As seaborn's barplot uses categorical positions, internally numbered 0,1,2,... both plots can be drawn on the same ax. This can be accomplished by recalculating the x values for the lineplot.
To obtain a legend, the label= keyword should be used. (Creating a legend on twinx axes is a bit more complicated and would involve creating custom handles.) Seaborn often automatically creates a legend. If you want to change its appearance, you can call ax1.legend(...) with customization parameters.
Here is some example code:
from pathlib import Path
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
import matplotlib.dates as mdates
import numpy as np
dfGroup = pd.DataFrame({
'Year': [1910, 1911, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920],
'Total Deaths': [0, 0, 2, 3, 2, 3, 4, 5, 6, 7, 8],
'Total Affected': [0, 1, 0, 2, 3, 6, 9, 8, 12, 13, 15]
})
# Add 3-year rolling average
dfGroup['rolling_3years'] = dfGroup['Total Deaths'].rolling(3).mean().shift(0)
dfGroup = dfGroup.fillna(0)
# Make a smooth line from the 3-year rolling average
from scipy.interpolate import make_interp_spline
X_Y_Spline = make_interp_spline(dfGroup['Year'], dfGroup['rolling_3years'])
# Returns evenly spaced numbers over a specified interval.
X_ = np.linspace(dfGroup['Year'].min(), dfGroup['Year'].max(), 500)
Y_ = X_Y_Spline(X_)
# Plot the data
a4_dims = (15, 10)
fig, ax1 = plt.subplots(figsize=a4_dims)
sns.barplot(x="Year", y="Total Deaths",
data=dfGroup, color='#42b7bd', label='Barplot label', ax=ax1)
x_plot = np.linspace(0, len(dfGroup) - 1, len(X_))
sns.lineplot(x=x_plot, y=Y_, marker='o', label='LinePlot label', ax=ax1)
ax1.set_ylim(ymin=0) # let bars touch the bottom of the plot
ax1.margins(x=0.02) # less margins left and right
# ax1.legend(title='legend title') # optionally change the legend
plt.show()
PS: If an ax is already created, it should be given as a parameter to seaborn's axes-level functions (so, sns.barplot(..., ax=ax1) instead of ax1 = sns.barplot(...).
I have two Arrays of positional Data (X,Y) and a corresponding 1D Array of Integers (Z) that weighs the positional Data. So my Data set looks like that:
X = [ 507, 1100, 1105, 1080, 378, 398, 373]
Y = [1047, 838, 821, 838, 644, 644, 659]
Z = [ 300, 55, 15, 15, 55, 15, 15]
I want to use that Data to create a KDE thats equivalent to a KDE that gets only X and Y as input but gets the X and Y values Z times. To apply that KDE to a np.mgrid to create a contourplot.
I already got it working by just iterating over the arrays in a FOR Loop and adding Z times X and Y, but that looks to me like a rather inelegant Solution and I hope you can help me to find a better way of doing this.
You could use the weights= parameter of scipy.stats.gaussian_kde:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
from scipy import stats
X = [ 507, 1100, 1105, 1080, 378, 398, 373]
Y = [1047, 838, 821, 838, 644, 644, 659]
Z = [ 300, 55, 15, 15, 55, 15, 15]
kernel = stats.gaussian_kde(np.array([X, Y]), weights=Z)
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
xs, ys = np.mgrid[0:1500:30j, 0:1500:30j]
zs = kernel(np.array([xs.ravel(), ys.ravel()])).reshape(xs.shape)
ax.plot_surface(xs, ys, zs, cmap="hot_r", lw=0.5, rstride=1, cstride=1, ec='k')
plt.show()
I check several different method, but why my curve can't be smoothed as what the others did? Here is my code and image.
from scipy.interpolate import splrep, splev
import matplotlib.pyplot as plt
list_x = [296, 297, 425, 460, 510, 532, 597, 601, 602, 611]
list_y = [2, 12, 67, 15, 21, 2037, 1995, 9, 39, 3]
bspl = splrep(list_x,list_y)
bspl_y = splev(list_x,bspl)
plt.figure()
plt.plot(list_x, bspl_y)
plt.xticks(fontsize = 10)
plt.yticks(fontsize = 10)
plt.show()
You don't see the interpolation, because you give matplotlib the same 10 data points for the interpolated curve that you use for your original data presentation. We have to create a higher resolution curve:
from scipy.interpolate import splrep, splev
import matplotlib.pyplot as plt
import numpy as np
list_x = [296, 297, 425, 460, 510, 521, 597, 601, 602, 611]
list_y = [2, 12, 67, 15, 21, 2037, 1995, 9, 39, 3]
bspl = splrep(list_x,list_y, s=0)
#values for the x axis
x_smooth = np.linspace(min(list_x), max(list_x), 1000)
#get y values from interpolated curve
bspl_y = splev(x_smooth, bspl)
plt.figure()
#original data points
plt.plot(list_x, list_y, 'rx-')
#and interpolated curve
plt.plot(x_smooth, bspl_y, 'b')
plt.xticks(fontsize = 10)
plt.yticks(fontsize = 10)
plt.show()
And this is the output we get:
So I've got some data which I wish to plot via a frequency density (unequal class width) histogram, and via some searching online, I've created this to allow me to do this.
import numpy as np
import matplotlib.pyplot as plt
plt.xkcd()
freqs = np.array([3221, 1890, 866, 529, 434, 494, 382, 92, 32, 7, 7])
bins = np.array([0, 5, 10, 15, 20, 30, 50, 100, 200, 500, 1000, 1500])
widths = bins[1:] - bins[:-1]
heights = freqs.astype(np.float)/widths
plt.xlabel('Cost in Pounds')
plt.ylabel('Frequency Density')
plt.fill_between(bins.repeat(2)[1:-1], heights.repeat(2), facecolor='steelblue')
plt.show()
As you may see however, this data stretches into the thousands on the x axis and on the y axis (density) goes from tiny data (<1) to vast data (>100). To solve this I will need to break both axis. The closest to help I've found so far is this, which I've found hard to use. Would you be able to help?
Thanks, Aj.
You could just use a bar plot. Setting the xtick labels to represent the bin values.
With logarithmic y scale
import numpy as np
import matplotlib.pyplot as plt
plt.xkcd()
fig, ax = plt.subplots()
freqs = np.array([3221, 1890, 866, 529, 434, 494, 382, 92, 32, 7, 7])
freqs = np.log10(freqs)
bins = np.array([0, 5, 10, 15, 20, 30, 50, 100, 200, 500, 1000, 1500])
width = 0.35
ind = np.arange(len(freqs))
rects1 = ax.bar(ind, freqs, width)
plt.xlabel('Cost in Pounds')
plt.ylabel('Frequency Density')
tick_labels = [ '{0} - {1}'.format(*bin) for bin in zip(bins[:-1], bins[1:])]
ax.set_xticks(ind+width)
ax.set_xticklabels(tick_labels)
fig.autofmt_xdate()
plt.show()