How to get arithmetically growing minor ticks with matplotlib? - python

The following snippet creates a list myHLines of (y) values that is arithmetically growing.
I want to use them as minor y ticks in a matplotlib plot.
How can I do this?
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame({'a': [1, 3, 10, 30, 100]})
myMin = df.a.min()
myMax = df.a.max()
ratio = 3
myHLines = [myMin * ratio ** i for i in range(1000) if myMin * ratio ** i < myMax]
print("myHLines=", myHLines)
# myHLines= [1, 3, 9, 27, 81]
plt.plot(df, '-o', markersize=2, c='r')
plt.show()

Is the scale of the y-axis you want to achieve the y-axis shown in the graph below?
plt.plot(df, '-o', markersize=2, c='r')
locs, labels = plt.yticks()
new_y = sorted(myHLines + locs.tolist()[1:-1])
# print(new_y)
plt.yticks(new_y)
plt.show()

Related

How do i extend trend line in matplotlib plot?

Here is a part of the plot that I have
I need to create TrendLine that would be extended to the 3th
quarter of this plot... I can's think of any solution.
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
x = [1, 8, 12, 20]
y = [1, 8.4, 12.5, 20]
fig = plt.figure(figsize=(20,20))
ax = fig.add_subplot()
ax.set_xlim(-30, 30)
ax.set_ylim(-20, 20)
plt.subplot().spines['left'].set_position('center')
plt.subplot().spines['bottom'].set_position('center')
plt.plot(x,y, 'b.', ms=20)
plt.minorticks_on()
ax.grid(True, which='both')
mean_line = ax.plot()
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
plt.plot(x,p(x),"r--")
plt.show()
I don't think reverse x and y would do the job, it would be limited to the poly1d that pass (0,0)
I think the extending method should be using the fitted line itself.
so a more general method is extend the x and use the poly1d(z) to calculate an extended line. z is description of the fitted line, so feeding x value to z would draw the line.
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')
x = [1, 8, 12, 20]
y = [1, 8.4, 12.5, 20]
# make an xx that with from -20 to 20
#xx =np.array(x)
#xx = sorted(np.concatenate((-xx, xx), axis=0))
xx = [-20, 20] # also work
fig, ax = plt.subplots(figsize=(10,10))
ax.set_xlim(-30, 30)
ax.set_ylim(-20, 20)
plt.subplot().spines['left'].set_position('center')
plt.subplot().spines['bottom'].set_position('center')
plt.subplot().spines['right'].set_color('none')
plt.subplot().spines['top'].set_color('none')
plt.plot(x,y, 'b.', ms=20)
plt.minorticks_on()
#ax.grid(True, which='both')
plt.subplot().grid(True, which='both')
mean_line = ax.plot()
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
plt.plot(xx,p(xx),"r--")
plt.show()
if you zoomin near the (0,0), you should see it's not passing the origin point.
zoomed in near (0,0)
result image
I don't have any experience with trendlines, but I created a composite of existing x and y values with different signs and drew the following graph.
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
x = [1, 8, 12, 20]
y = [1, 8.4, 12.5, 20]
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot()
ax.set_xlim(-30, 30)
ax.set_ylim(-20, 20)
plt.subplot().spines['left'].set_position('center')
plt.subplot().spines['bottom'].set_position('center')
plt.plot(x,y, 'b.', ms=20)
plt.minorticks_on()
ax.grid(True, which='both')
mean_line = ax.plot()
# update
xx =np.array(x)
xx = sorted(np.concatenate((-xx, xx), axis=0))
yy =np.array(y)
yy = sorted(np.concatenate((-yy, yy), axis=0))
z = np.polyfit(xx, yy, 1)
p = np.poly1d(z)
plt.plot(xx,p(xx),"r--")
plt.show()

Replicate Log10 Scaling with Matplotlib

I'm trying to recreate a plot that has the y-axis styled as so:
But can't seem to figure out how to get the axis breaks and labels lined up how I want them. I am currently doing this in my code:
# plot lines
for key, group in grouped:
plt.plot(group.x * 950, np.log10(group.y), label=key)
# plot points
exp_group = exp_data.groupby('Experiment')
for key, group in exp_group:
plt.plot(group.x, np.log10(group.y), label=key, marker='o')
plt.yticks(np.arange(-3, 3), label=10.0**np.arange(-3,3))
plt.show()
A solution is to use plt.yticks:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 1000)
y = np.exp(x) - x**3 + x**2 - x**(1/2)
plt.figure()
plt.semilogy(x, y)
locs, labels = plt.yticks()
print(locs)
print(labels)
lst_10 = [1, 10, 100, 1000, 10000]
plt.yticks(lst_10, lst_10)
plt.title('Different label on y-axis')
plt.figure()
plt.semilogy(x, y)
plt.title('Default label')
plt.show()
The function plt.yticks takes two arguments, the locations and the labels. I want the labels in the locations 1, 10, 100, 1000, 10000; I want the label (in location 1) to have a label 1, the label (in localtion 10) to have a label 10, and so on. I have also used plt.semilogy to get that semi-log axis.

Got more ylabels than specified by set_yticklabel() [duplicate]

Here is a simple plot:
1) How to disable the ticks?
2) How to reduce their number?
Here is a sample code:
from pylab import *
import numpy as np
x = [5e-05, 5e-06, 5e-07, 5e-08, 5e-09, 5e-10]
y = [-13, 14, 100, 120, 105, 93]
def myfunc(x,p):
sl,yt,yb,ec=p
y = yb + (yt-yb)/(1+np.power(10, sl*(np.log10(x)-np.log10(ec))))
return y
xp = np.power(10, np.linspace(np.log10(min(x)/10), np.log10(max(x)*10), 100))
pxp=myfunc(xp, [1,100,0,1e-6])
subplot(111,axisbg="#dfdfdf")
plt.plot(x, y, '.', xp, pxp, 'g-', linewidth=1)
plt.xscale('log')
plt.grid(True,ls="-", linewidth=0.4, color="#ffffff", alpha=0.5)
plt.draw()
plt.show()
Which produces:
plt.minorticks_off()
Turns em off!
To change the number of them/position them, you can use the subsx parameter. like this:
plt.xscale('log', subsx=[2, 3, 4, 5, 6, 7, 8, 9])
From the docs:
subsx/subsy: Where to place the subticks between each major tick.
Should be a sequence of integers. For example, in a log10 scale: [2,
3, 4, 5, 6, 7, 8, 9]
will place 8 logarithmically spaced minor ticks between each major
tick.
Calling plt.minorticks_off() will apply this to the current axis. (The function is actually a wrapper to gca().minorticks_off().)
You can also apply this to an individual axis in the same way:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.minorticks_off()
from pylab import *
import numpy as np
x = [5e-05, 5e-06, 5e-07, 5e-08, 5e-09, 5e-10]
y = [-13, 14, 100, 120, 105, 93]
def myfunc(x,p):
sl,yt,yb,ec=p
y = yb + (yt-yb)/(1+np.power(10, sl*(np.log10(x)-np.log10(ec))))
return y
xp = np.power(10, np.linspace(np.log10(min(x)/10), np.log10(max(x)*10), 100))
pxp=myfunc(xp, [1,100,0,1e-6])
ax=subplot(111,axisbg="#dfdfdf")
plt.plot(x, y, '.', xp, pxp, 'g-', linewidth=1)
plt.xscale('log')
plt.grid(True,ls="-", linewidth=0.4, color="#ffffff", alpha=0.5)
plt.minorticks_off() # turns off minor ticks
plt.draw()
plt.show()

change position of top major x-ticks as function of bottom axis

I want to something similar to How to add a second x-axis in matplotlib, i.e. have a top x-axis that displays a wavelength and a bottom axis that displays the corresponding frequency.
Reproducing linked example gives me a plot that looks like this:
This plot was produced with:
#setting up the plot
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.gridspec as gridspec
fig = plt.figure()
fig.tight_layout()
ax = plt.subplot()
#Here it gets interesting!
def tick_function(X):
c = 299792458
V = c/X
V = V*1e6
V = np.round(V,0)
V[2] = 3000
V = V.astype(int)
return(V)
ax = plt.subplot()
ax_top = ax.twiny()
ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
ax_top.set_xscale("log", nonposx='clip')
ax.set_xlim([8e10,5e14])
ax.set_ylim([5e33,2e36])
axTicks = ax.get_xticks()
ax_top_Ticks = axTicks
ax_top.set_xticks(ax_top_Ticks)
ax_top.set_xlim(ax.get_xlim())
ax_top.set_xbound(ax.get_xbound())
ax_top.set_xticklabels(tick_function(ax_top_Ticks))
Now, rather than plotting the top major x-ticks at the position of the bottom major x-axis, I'd like to have them shifted.
I.e., I would like to have the top major x-ticks at positions 1000, 100, 10, 1 and the minor ticks shifted accordingly.
This is what I'd like it too look like:
I found this plot, that's what I want!
http://inspirehep.net/record/877424/files/fig2.png
Note, since lambda=c/f and ax & ax_top are logarithmic the spacing of the minor ticks has to be inverted to!
The trick is to choose the wavelengths you want and convert them to frequencies. Then use those frequencies as positions for the upper ticks.
#setting up the plot
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure()
ax = plt.subplot()
def conversion_freq_lam(inp):
c = 299792458
outp = c/inp
outp = outp.astype(int)
return outp
#ax = plt.subplot(gs1[0])
ax = plt.subplot(111)
ax_top = ax.twiny()
ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
ax_top.set_xscale("log", nonposx='clip')
ax.set_xlim([8e10,5e14])
ax.set_ylim([5e33,2e36])
goal_lambdas = np.array([100000, 10000, 1000, 100, 10, 1, 0.1, 0.01])
goal_freqs = conversion_freq_lam(goal_lambdas)
ax_top_Ticks = goal_freqs * 1e6 # magic factor 1e6 from your attempt. Units?
ax_top.set_xticks(ax_top_Ticks)
ax_top.set_xlim(ax.get_xlim())
ax_top.set_xbound(ax.get_xbound())
ax_top.set_xticklabels(goal_lambdas)
plt.savefig('test_2axes.png')
This produces the following plot:
The magic number 1e6 used as a scaling factor I took from your question. I assume it is caused by the units of the axis.
Edit:
To have correctly spaced minor ticks at the top axis (for example at 2, 3, 4, ..., 20, 30, 40, 50, ...) add the following code block:
def find_minor_vals(goals):
minors = []
factors = np.arange(2, 10, 1)
for val in goals:
minors.extend(list(val * factors))
print minors
return np.array(minors)
goal_lambdas_minor = find_minor_vals(goal_lambdas)
goal_freqs_minor = conversion_freq_lam(goal_lambdas_minor) * 1e6
minor_locator = FixedLocator(goal_freqs_minor)
ax_top.xaxis.set_minor_locator(minor_locator)
Which results in the following picture:

Python matplotlib divide scatterplot into categories based on slope

I have a list of x and a list of y values. I'd like to construct a scatterplot in Matplotlib and divide the dots into five categories based on their x and y coordinates, like in the image below:
angles = [0, 18, 36, 54, 72, 90]
colors = ['r','g','b','c']
x = [....]
y = [....]
All of the points in the divided category will be the same color. It would also be great to have a legend for the categories. I am new to Matplotlib and Python, does anyone know how I can approach this?
Here's a working example which will give you a little idea to get started:
from matplotlib import pyplot as plt
from matplotlib.lines import Line2D
import math
import random
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
w, h = 7, 5
a = [[random.random() * w, random.random() * h] for i in range(100)]
plt.plot(*zip(*a), marker='o', color='r', ls='')
for deg in [18, 36, 54]:
r = 10
line = Line2D([0, r * math.cos(math.radians(deg))],
[0, r * math.sin(math.radians(deg))],
linewidth=1, linestyle="-", color="green")
ax.add_line(line)
ax.set_xlim(0, w)
ax.set_ylim(0, h)
plt.legend()
plt.show()

Categories