Matplotlib Bar Graph Overlapping of Bars - python

So I have created a bar graph but I am having trouble with overlapping bars. I thought that the problem was with the edges overlapping, but when I changed edges='none'the bars were just really slim.
I do believe my math is correct in assuming that 3 bars with a width of of .3 should be moving along the x axis by .3 and leaving a .1 gap between each set of bars. (Note x is increasing by 1)
I don't see why the bars end up overlapping and the overlap seems to get worse near the end of the graph.
ax = plt.subplot(111)
ax2 = ax.twinx()
ax.bar(x,EWtot,width=.3, color='b', align='center',label='Eyewall',edgecolor='b')
ax.bar(x + 0.3,ICtot,width=.3, color='g', align='center',label='Inner Core',edgecolor='g')
ax.bar(x + 0.6,RBtot,width=.3, color='r', align='center',label='RainBand',edgecolor='r')

I think its a combination of two factors. First you could increase the precision of your width and x-position a little by supplying some extra digits, or specifying them as a fraction.
Secondly the use of the edge (linewidth > 0) makes the bars overlap a little by default, the edge is centered, so half the edge is inside the bar, the other half outside. Disabling the edge entirely prevents any overlap.
Increasing the linewidth and setting an alpha might help you to identify whats going on exactly.
The example below illustrates this:
n = 10
x = np.arange(n)
a = np.random.rand(n)
b = np.random.rand(n)
c = np.random.rand(n)
fig, axs = plt.subplots(2,1,figsize=(10,8))
axs[0].bar(x, a, width=0.3, facecolor='b', edgecolor='b', linewidth=3, alpha=.5)
axs[0].bar(x+0.3, b, width=0.3, facecolor='r', edgecolor='r', linewidth=3, alpha=.5)
axs[0].bar(x+0.6, c, width=0.3, facecolor='g', edgecolor='g', linewidth=3, alpha=.5)
axs[1].bar(x, a, width=1/3, facecolor='b', alpha=.5, linewidth=0)
axs[1].bar(x+1/3, b, width=1/3, facecolor='r', alpha=.5, linewidth=0)
axs[1].bar(x+2/3, c, width=1/3, facecolor='g', alpha=.5, linewidth=0)

Related

How to fix transparency overlaps in Matplotlib when plotting multiple figures?

I have a function that inputs a string (the name of the dataframe we're visualizing) and returns two histograms that visualize that data. The first plot (on the left) is the raw data, the one on the right is it after being normalized (same, just plotted using the matplotlib parameter density=True). But as you can see, this leads to transparency issues when the plots overlap. This is my code for this particular plot:
plt.rcParams["figure.figsize"] = [12, 8]
plt.rcParams["figure.autolayout"] = True
ax0_1 = plt.subplot(121)
_,bins,_ = ax0_1.hist(filtered_0,alpha=1,color='b',bins=15,label='All apples')
ax0_1.hist(filtered_1,alpha=0.9,color='gold',bins=bins,label='Less than two apples')
ax0_1.set_title('Condition 0 vs Condition 1: '+'{}'.format(apple_data),fontsize=14)
ax0_1.set_xlabel('{}'.format(apple_data),fontsize=13)
ax0_1.set_ylabel('Frequency',fontsize=13)
ax0_1.grid(axis='y',linewidth=0.4)
ax0_1.tick_params(axis='x',labelsize=13)
ax0_1.tick_params(axis='y',labelsize=13)
ax0_1_norm = plt.subplot(122)
_,bins,_ = ax0_1_norm.hist(filtered_0,alpha=1,color='b',bins=15,label='All apples',density=True)
ax0_1_norm.hist(filtered_1,alpha=0.9,color='gold',bins=bins,label='Less than two apples',density=True)
ax0_1_norm.set_title('Condition 0 vs Condition 1: '+'{} - Normalized'.format(apple_data),fontsize=14)
ax0_1_norm.set_xlabel('{}'.format(apple_data),fontsize=13)
ax0_1_norm.set_ylabel('Frequency',fontsize=13)
ax0_1_norm.legend(bbox_to_anchor=(2, 0.95))
ax0_1_norm.grid(axis='y',linewidth=0.4)
ax0_1_norm.tick_params(axis='x',labelsize=13)
ax0_1_norm.tick_params(axis='y',labelsize=13)
plt.tight_layout(pad=0.5)
plt.show()
What my current plot looks like
Any ideas on how to make the colors blend a bit better would be helpful. Alternatively, if there are any other combinations you know of that would work instead, feel free to share. I'm not picky about the color choice. Thanks!
I think it is better to emphasize such a histogram by distinguishing it by the shape of the histogram or by the difference in transparency rather than visualizing it by color. I have coded an example from the official reference with additional overlap.
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(20211021)
N_points = 100000
n_bins = 20
x = np.random.randn(N_points)
y = .4 * x + np.random.randn(100000) + 2
fig, axs = plt.subplots(2, 2, sharey=True, tight_layout=True)
# We can set the number of bins with the `bins` kwarg
axs[0,0].hist(x, color='b', alpha=0.9, bins=n_bins, ec='b', fc='None')
axs[0,0].hist(y, color='gold', alpha=0.6, bins=21)
axs[0,0].set_title('edgecolor and facecolor None')
axs[0,1].hist(x, color='b', alpha=0.9, bins=n_bins)
axs[0,1].hist(y, color='gold', alpha=0.6, bins=21, ec='b')
axs[0,1].set_title('edgecolor and facecolor')
axs[1,0].hist(x, alpha=0.9, bins=n_bins, histtype='step', facecolor='b')
axs[1,0].hist(y, color='gold', alpha=0.6, bins=21)
axs[1,0].set_title('step')
axs[1,1].hist(x, color='b', alpha=0.9, bins=n_bins, histtype='bar', rwidth=0.8)
axs[1,1].hist(y, color='gold', alpha=0.6, bins=21, ec='b')
axs[1,1].set_title('bar')
plt.show()

matplotlib: different handletextpad values in the same legend

I have the following script:
fig = plt.figure()
fig.set_size_inches(8,7)
ax1 = fig.add_subplot(1,1,1)
### PLOT
for k in my_dict:
x, y = my_dict[k][0], my_dict[k][1]
ax1.plot(x, y, linewidth = 0, marker='o', markersize = 4)
X = np.logspace(0.3, 6.6)
ax1.plot(X, 2*X**(-2), linewidth = 2, c='k')
X = np.logspace(0.3, 7.7)
ax1.plot(X, 3*X**(-1.5), linewidth = 2, c='b')
ax1.set_xscale('log')
ax1.set_yscale('log')
## LEGEND
labels = ['$10^{-1} \, \\Delta_1^*$', '$\\Delta_1^*$',\
'$10^{5/2} \, \\Delta_1^*$', '$10^3 \, \\Delta_1^*$',
'$x^{-2}$', '$x^{-3/2}$']
curves = ax1.get_lines()
legend1 = ax1.legend([curves[0], curves[1], curves[2]],\
[labels[0], labels[1], labels[2]],\
loc=1, ncol=1, fancybox=False, shadow=False,\
framealpha=0.0, markerscale=2, fontsize=25, handletextpad=0.0)
legend2 = ax1.legend([curves[3], curves[4], curves[5]],\
[labels[3], labels[4], labels[5]],\
loc=3, ncol=1, fancybox=False, shadow=False,\
framealpha=0.0, markerscale=2, fontsize=25, handletextpad=0.0)
vp = legend1._legend_box._children[-1]._children[0]
for c in vp._children:
c._children.reverse()
vp.align="right"
ax1.add_artist(legend1)
ax1.add_artist(legend2)
fig.tight_layout()
plt.show()
The result is
The issue: I use handletextpad in the legends, and that is because I need points and text to be very close. However the last two elements in the second legend are not points, but lines. They take more space then points and the text happens to be too close.
I need to keep this distance between text and points while increasing the distance between text and lines in the same legend.
I tried with handletextpad=[0.1, 0.5, 0.5] and similar strategies, but I haven't been able to set individual values of handletextpad.
Another possibility would be to make separate legends and specifically one with only lines. This, however, would force me to manually positioning any legend very carefully and I'd rather not doing it. Also (I don't know if it can help), but I'd rather not replace
plt.plot(x, y, linewidth = 0, markersize = 4)
with
plt.scatter(x, y).
Except for these two caveats, everything is welcome.

In matplotlib, pyplot.text disappears when applying twinx. How can it appear?

plt.text(x, y, text) is working when I use single y-axis but it disappears when I apply twinx. Can it be fixed?
fig, ax = plt.figure(figsize=(8,6))
text = "E_rms(test) = {:7.3f}/nE_maxres = {:7.3f}".format(rmse,maxres)
plt.text(0,0,text)
plt.xlabel('data')
ax.set_ylable('PE(eV)')
ax.tick_params(axis='y', labelcolor='b')
if Ltwinx:
ax2 = ax.twinx()
ax2.set_ylabel("Difference(eV)")
ax2.set_tick_params(axis='y', labelcolor='g')
p1 = ax.scatter(range(y), y, c='r', label='true')
p2 = ax.scatter(range(y), h, c='b', label='hypothesis')
if Ltwinx:
p3, = ax2.plot(range(y), diff, c='g', label='difference')
plt.legend([p1,p2],['true value', 'hypothesis'],loc=(0.0, 0.1))
else:
p3, = plt.plot(range(y), diff, c='g', label='difference')
plt.legend([p1,p2,p3],['true value', 'hypothesis', 'difference'],loc=(0.0, 0.1))
plt.show()
This code is an extraction from full code and the belows are figure with two y-axes (1st figure) and single y-axis, where text disappeared in twinx (1st figure). Note y-axis scale is different due to the values of "diff" though p1, p2 figures are same in both figures.
I have misunderstood the manual for matplotlib.pyplot.text.
To use axis coordinate such as (0,0) to (1,1) regardless of y-values, I should add keyword of "transform=ax.transAxes". So
plt.text(0,0,text, transform=ax.transAxes)
is working.

Scaling Y and X axis python graph

I have set of data and I made a graph by using them. The problem is the data does not look like scaled properly since y axis ranges from 0 to 30000 while x axis from -2 to 30. How can I solve this problem ? Thanks
Here is my code,
import numpy as np
import matplotlib.pyplot as plt
voltage365nm = [-1.877,-2.0,-1.5,-1.0,0.0,5.0,10.0,20.0,30.0]
voltage405nm = [-1.437,-2.0,-1.5,-1.0,0.0,5.0,10.0,20.0,30.0]
voltage546nm = [-0.768,-2.0,-1.5,-1.0,0.0,5.0,10.0,20.0,30.0]
current365nm = [0.0,5.6,151.1,428,1164,5760,9870,1626,20700]
current405nm = [0.0,-8.2,-2.6,70.2,278,1954,2460,3970,5021]
current546nm = [0.0,-6.5,-6.1,-5.4,248,1435,2240,3250,3750] plt.plot(voltage365nm,current405nm,"r--",marker="s",label="$\lambda$=365 nm")
plt.plot(voltage405nm,current405nm,"b-.",marker="o",label="$\lambda$=405nm")
plt.plot(voltage546nm,current546nm,"g-",marker="^",label="$\lambda$=546nm")
plt.legend(loc='best')
plt.xlabel("Voltage (V)")
plt.ylabel("Current (I x $10^{-13}A}$)")
plt.title("Current vs Voltage")
plt.grid(b=True, which='major', color='g', linestyle='--')
plt.grid(b=True, which='minor', color='r', linestyle='--', alpha=0.2)
plt.show()
You could use plt.xlim([-10,0]) and plt.ylim([-10,0]) to specify the minimum and maximum values of your axes.
I ran your code, and got the following chart:
Is your concern that the data points along the lower end of your y axis are "scrunched up"? If that's the case, perhaps plotting on the log axis might help. Use the set_yscale('log') method like so:
ax = plt.gca()
ax.set_yscale('log')
With that, I get the following chart:
The issue with this of course is that some of the y-axis values are negative, and thus can't be directly plotted on a log scale. The complete solution would involve adding a constant to all currents such that they're positive.
PS -- I think there's a bug in one of your plt.plot commands:
plt.plot(voltage365nm,current405nm,"r--",marker="s",label="$\lambda$=365 nm")
should be
plt.plot(voltage365nm, current365nm,"r--",marker="s",label="$\lambda$=365 nm")
I would additionally put the lower values in an inset as a zoom in
# your existing code before plt.show()
left, bottom, width, height = [0.32, 0.55, 0.4, 0.3]
ax2 = fig.add_axes([left, bottom, width, height])
ax2.plot(voltage365nm,current365nm,"r--",marker="s",label="$\lambda$=365 nm")
ax2.plot(voltage405nm,current405nm,"b-.",marker="o",label="$\lambda$=405nm")
ax2.plot(voltage546nm,current546nm,"g-",marker="^",label="$\lambda$=546nm")
ax2.set_xlim(-2.1, 1)
ax2.set_ylim(-100, 1500)
plt.grid(b=True, which='major', color='g', linestyle='--')
plt.show()

Bar plot with polar axis

I want to make my plot similar to this-
Currently, what I am able to get is this:
I am not able to do the following things:
Get the time labels inside the inner circle
Get the ticks inside the circle. This question - How to create minor ticks for polar plot matplotlib, had an idea which I tried but it was messing up with other parts of the plot.
Time labels only work properly when the step size is 3. For other steps, it does not align with the bars.
Below is the reproducible code to produce the plot
arr = np.random.randint(0, 24, size = 50000)
df = pd.DataFrame({"COL": arr}).COL.value_counts().sort_index()
N = 24
bottom = 1000
theta, width = np.linspace(0.0, 2 * np.pi, N, endpoint=False, retstep=True)
plt.figure(figsize = (10, 6))
ax = plt.subplot(111, polar=True)
bars = ax.bar(
theta, df,
width=width-0.03,
bottom=bottom,
color="#f39c12", edgecolor="black"
)
bars = ax.bar(
theta, [3000]*24,
width=width-0.03,
bottom=bottom,
color="#f39c12", alpha=0.2
)
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ax.grid(False)
ax.spines['polar'].set_visible(False)
ax.set_rticks([])
ticks = [f"{i}:00" for i in range(0, 24, 3)]
ax.set_xticklabels(ticks)
_ = ax
I just drew two lines and a circle to make the "clock icon"
## Draw a "clock" icon inside of the graph
##lines for hands of a clock
x1, y1 = [0, 90], [0, 0.5*bottom]
x2, y2 = [0,0], [0, 0.5*bottom]
plt.plot(x1, y1, x2, y2, linewidth=2.5, solid_capstyle='round', color='#0066ff', alpha=1)
##circle for clockface
circle = pl.Circle((0, 0), 0.65*bottom, transform=ax.transData._b, linewidth=3, fill=False, color="#0066ff", alpha=1)
ax.add_artist(circle)
did you ever figure out how to write the numbers inside of the clock?
Also if you wanna add the numbers inside of the circle do this:
#00, 06, 12, 18 labels for clockface
plt.text(0, bottom*.80, "00", transform=ax.transData._b, ha='center', va='center', color='black', fontsize=2.5, fontweight='bold')
plt.text(bottom*.80, 0, "06", transform=ax.transData._b, ha='center', va='center', color='black', fontsize=2.5, fontweight='bold')
plt.text(0, -bottom*.80, "12", transform=ax.transData._b, ha='center', va='center', color='black', fontsize=2.5, fontweight='bold')
plt.text(-bottom*.80, 0, "18", transform=ax.transData._b, ha='center', va='center', color='black', fontsize=2.5, fontweight='bold')
I know it's not the best or fanciest way to do this but it works OK! Please # me if you have a cleaner way to write this code haha..
Also some other notes:
-For the code i posted above change the sizes of the hour and minute hand to make it look even better (multiply bottom variable by different values below 1)
-I had to change the fontsize, and linewidths as I changed the size and dpi of the image to make it look right but its not too hard to figure out how to make it look right.
-I ended up not drawing the tick marks as it didn't seem worth my time to figure out how to do it. would love to know from anyone else if there's some way to space out dashes or something around there. But i really just didn't wanna mess with the rotation

Categories

Resources