Bar plot with polar axis - python

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

Related

change the ticks of a chart

I can't make a change in the ticks, and either the whole graph turns white or the texts are grouped together and since the date is cyclic, it has the same matches on different days and that makes the graph messy
fig, ax = plt.subplots(sharex=True, constrained_layout=True,
figsize=(20, 12))
ax.plot(incra['Temp'], "-b", label='Temp')
ax.plot(incra['Hum'], "-k", label='Temp')
ax.set_ylabel('CO2 ppm')
ax.yaxis.grid(b=True, color='black', alpha=0.3, linestyle='-.', linewidth=1)
ax.xaxis.grid(b=True, color='black', alpha=0.3, linestyle='-.', linewidth=1)
ax.set_xticklabels(labels=incra['Date'], rotation = 45)
plt.savefig('po.png', format='png')[enter image description here](https://i.stack.imgur.com/0VbNX.png)
I have a large dataset, but I only want a few matches on the x-axis, how can I change my code to optimize for this?
x_ticks = [0, 50, 100, 150, 200]
ax.set_xticks(x_ticks)
ax.set_xticklabels(labels=incra['Date'][x_ticks], rotation = 45)

How can I avoid color overlap in matplot python?

I am trying to make a clear visualizatin of my data.
This is my code:
width = 0.5
plt.figure(figsize=(10,8))
counts1, bins, bars = plt.hist(data=df1_small, x='fz', bins=np.arange(-5, 5.5, width), color='dodgerblue', alpha=0.3) #rwidth can change the space between bars
#plt.plot(bins[:-1] + width/2, counts1, color='#FF6103', linewidth=2)
counts2, bins, bars = plt.hist(data=df2_big, x='fz', bins=np.arange(-5, 5.5, width), color='red', alpha=0.1)
#plt.plot(bins[:-1] + width/2, counts2, color='#76EEC6', linewidth=2)
labels = ["small", "big"]
plt.legend(labels)
plt.grid(False)
#plt.savefig("./figure/acce.eps", dpi=300)
plt.savefig("./figure/acce.png", dpi=300)
plt.show()
Whenever I plot, I found that color is overlapped if plot is overlapped.
Is there any idea to avoid this overlap?
This is my plot right now:
I want to avoid color overlapping and make it clear with just two colors.
Thank you
If you want to avoid any overlap between the two histograms, you could plot both in separate subplots. In this case, I think it makes more sense to title the axes than to add labels, but you could do either. See below:
# Create two subplots, aligned horizontally
fig, axes = plt.subplots(1, 2, figsize=(10, 8))
width = 0.5
# Plot each histogram in its own axis
counts1, bins, bars = axes[0].hist(data=df1_small, x='fz', bins=np.arange(-5, 5.5, width), color='dodgerblue', alpha=0.3) #rwidth can change the space between bars
counts2, bins, bars = axes[1].hist(data=df2_big, x='fz', bins=np.arange(-5, 5.5, width), color='red', alpha=0.1)
# Add title
axes[0].set_title("small")
axes[1].set_title("small")
# Add labels
axes[0].legend("small")
axes[1].legend("big")
# Need to disable the grid for both axes
axes[0].grid(False)
axes[1].grid(False)
plt.savefig("./figure/acce.png", dpi=300)
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.

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()

Matplotlib Bar Graph Overlapping of Bars

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)

Categories

Resources