I am using matplotlib for a graphing application. I am trying to create a graph which has strings as the X values. However, the using plot function expects a numeric value for X.
How can I use string X values?
From matplotlib 2.1 on you can use strings in plotting functions.
import matplotlib.pyplot as plt
x = ["Apple", "Banana", "Cherry"]
y = [5,2,3]
plt.plot(x, y)
plt.show()
Note that in order to preserve the order of the input strings on the plot you need to use matplotlib >=2.2.
You should try xticks
import pylab
names = ['anne','barbara','cathy']
counts = [3230,2002,5456]
pylab.figure(1)
x = range(3)
pylab.xticks(x, names)
pylab.plot(x,counts,"g")
pylab.show()
Why not just make the x value some auto-incrementing number and then change the label?
--jed
Here's one way which i know works, though i would think creating custom symbols is a more natural way accomplish this.
from matplotlib import pyplot as PLT
# make up some data for this example
t = range(8)
s = 7 * t + 5
# make up some data labels which we want to appear in place of the symbols
x = 8 * "dp".split()
y = map(str, range(8))
data_labels = [ i+j for i, j in zip(x, y)]
fig = PLT.figure()
ax1 = fig.add_subplot(111)
ax1.plot(t, s, "o", mfc="#FFFFFF") # set the symbol color so they are invisible
for a, b, c in zip(t, s, data_labels) :
ax1.text(a, b, c, color="green")
PLT.show()
So this puts "dp1", "dp2",... in place of each of the original data symbols--in essence creating custom "text symbols" though again i have to believe there's a more direct way to do this in matplotlib (without using Artists).
I couldn't find a convenient way to accomplish that, so I resorted to this little helper function.
import matplotlib.pyplot as p
def plot_classes(x, y, plotfun=p.scatter, **kwargs):
from itertools import count
import numpy as np
classes = sorted(set(x))
class_dict = dict(zip(classes, count()))
class_map = lambda x: class_dict[x]
plotfun(map(class_map, x), y, **kwargs)
p.xticks(np.arange(len(classes)), classes)
Then, calling plot_classes(data["class"], data["y"], marker="+") should work as expected.
Related
I'm new to python and i am trying to plot 3 exponential functions on the same axis without using NumPy (not allowed). MatPlotLib, SymPy etc are allowed.
Question answered with broad answers below.
Code removed for privacy - this is not needed to understand the answers below as they are broad, or to answer any future questions on this topic
Of course, and I hope that you can understand my scruples, I prefer to leave my answer as generic as possible while trying to help you.
If you cannot use Numpy1, you have to use the math module and old good lists.
You start importing math from the standard library and the pyplot module from Matplotlib:
import math
import matplotlib.pyplot as plt
You decide the interval in which you plot your function and how many points you need for your plot
x_0, x_N = 0, 12
N =120
N is best intended as the number of intervals between N+1 points, so that we write
dx = (x_N-x_0)/N
Now we can say x_i = x_0 + dx × i but we have to store our results so that they are reusable. It's now that we have to use a list and we have two options, ① start with an empty list and append to it all the x_i using a for loop
xs = []
for i in range(N+1): xs.append(x_0+dx*i)
and ② a list comprehension
xs = [ x_0+dx*i for i in range(N+1) ]
(the result is identical).
You now have fixed the problem of the abscissae, it's the turn of the ordinates; again, we can use the append or the list comprehension
ys = []
for i in range(N+1):
x = xs[i]
y = math.sin(x/3.805)
ys.append(y)
or
ys = [ math.sin(xs[i]/3.805) for i in range(N+1) ]
Now you can plot the function
plt.plot(xs, ys, label='sin(%.3fx)'%(1/3.805))
plt.legend()
plt.grid()
plt.show()
(1) You cannot use Numpy but, but Matplotlib will use Numpy behind the scenes...
The lists that you pass to plt.plot are immediately converted to Numpy arrays! and only later are processed by the complex machinery of the plotting module.
Here is an approach using sympy, Python's package for symbolic math. It first solves x* to be log(delta/2)/a. Then, for some given values, a plot is drawn.
Note that sympy has a very simplified plotting function, with limited control over legend placement etc. If you need more control, the function values need to be calculated in an array.
from sympy import *
from sympy.abc import x, a, b
delta = symbols('delta', real=True)
x_star = symbols('x*', real=True)
f = exp(a*x)
g = -exp(a*x)
h = exp(a*x)*sin(b*x)
eq = Eq(delta, f.subs(x, x_star) - g.subs(x, x_star))
sol = solve(eq, x_star) # [log(delta/2)/a]
values = {a: 0.5, b: 1.5, delta:4.0}
x_star = sol[0].subs(values)
p = plot(f.subs(values), g.subs(values), h.subs(values), (x, 0, x_star),
show=False, legend=True, ylabel='', ylim=(-2,3.5))
p[0].line_color = 'r'
p[1].line_color = 'g'
p[2].line_color = 'b'
p.show()
Resulting plot:
Here is your version with some small adaptions to make it work. Note that matplotlib's plot functions work a little bit different than sympy's.
import matplotlib.pyplot as plt
from math import exp, sin
a = 5.0
b = 10.0
d = 0.1
x_star = 6.0
#x_star =(1/a)*float((math.log1p(d/2)))# #x* evenually needs to be in this form*#
print('x*= ',x_star)
steps = 200; r = [i*x_star/steps for i in range(steps)] # similar to np.linspace
f_r = []
g_r = []
h_r = []
for x in r:
y = exp(a*x)
f = y
f_r.append(f)
print('f(x)= ',f)
g = -1*y
g_r.append(g)
print('g(x)= ',g)
h = y*sin(b*x)
h_r.append(h)
print('h(x)= ',h)
plt.plot(r, f_r, 'b--', linewidth=1, color='r', label='f(x)=exp(a*x)')
plt.plot(r, g_r, 'b--', linewidth=2, color='g', label='g(x)=-exp(a*x)')
plt.plot(r, h_r, 'b--', linewidth=3, color='b', label='h(x)=exp(a*x)*sin(b*x)')
plt.ylabel('values')
plt.xlabel('x')
plt.legend(title='functions')
plt.show()
I am trying to plot (x,y) where as y = [[1,2,3],[4,5,6],[7,8,9]].
Say, len(x) = len(y[1]) = len(y[2])..
The length of the y is decided by the User input. I want to plot multiple plots of y in the same graph i.e, (x, y[1],y[2],y[3],...). When I tried using loop it says dimension error.
I also tried: plt.plot(x,y[i] for i in range(1,len(y)))
How do I plot ? Please help.
for i in range(1,len(y)):
plt.plot(x,y[i],label = 'id %s'%i)
plt.legend()
plt.show()
Assuming some sample values for x, below is the code that could give you the desired output.
import matplotlib.pyplot as plt
x = [1,2,3]
y = [[1,2,3],[4,5,6],[7,8,9]]
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.title("A test graph")
for i in range(len(y[0])):
plt.plot(x,[pt[i] for pt in y],label = 'id %s'%i)
plt.legend()
plt.show()
Assumptions: x and any element in y are of the same length.
The idea is reading element by element so as to construct the list (x,y[0]'s), (x,y[1]'s) and (x,y[n]'s.
Edited: Adapt the code if y contains more lists.
Below is the plot I get for this case:
Use a for loop to generate the plots and use the .show() method after the for loop.
import matplotlib.pyplot as plt
for impacts in impactData:
timefilteredForce = plt.plot(impacts)
timefilteredForce = plt.xlabel('points')
timefilteredForce = plt.ylabel('Force')
plt.show()
impactData is a list of lists.
I have lists of ~10 corresponding input files containing columns of tab separated data approx 300 lines/datapoints each.
I'm looking to plot the contents of each set of data such that I have a 2 plots for each set of data one is simply of x vs (y1,y2,y3,...) and one which is transformed by a function e.g. x vs (f(y1), f(y2),f(y3),...).
I am not sure of the best way to achieve it, I thought about using a simple array of filenames then couldn't work out how to store them all without overwriting the data - something like this:
import numpy as np
import matplotlib.pyplot as plt
def ReadDataFile(file):
print (file)
x,y = np.loadtxt(file, unpack=True, usecols=(8,9))
return x, y
inputFiles = ['data1.txt','data2.txt','data2.txt',...]
for file in inputFiles:
x1,y1 = ReadDataFile(file) ## ? ##
p1,q1 = function(x1,y1) ## ? ##
plt.figure(1)
plt.plot(x1,y1)
plt.plot(x2,y2)
...
# plt.savefig(...)
plt.figure(2)
plt.plot(p1,q1)
plt.plot(p2,q2)
...
# plt.savefig(...)
plt.show()
I guess my question is how to best read and store all the data and maintain tha ability to access it without needing to put all the code in the readloop. Can I read two data sets into a list of pairs? Is that a thing in Python? if so, how do I access them?
Thanks in advance for any help!
Best regards!
Basically, I think you should put all your code in the readloop, because that will work easily. There's a slightly different way of using matplotlib that makes it easy to use the existing organization of your data AND write shorter code. Here's a toy, but complete, example:
import matplotlib.pyplot as plt
from numpy.random import random
fig, axs = plt.subplots(2)
for c in 'abc': # In your case, for filename in [file-list]:
x, y = random((2, 5))
axs[0].plot(x, y, label=c) # filename instead of c in your case
axs[1].plot(x, y**2, label=c) # Plot p(x,y), q(x,y) in your case
axs[0].legend() # handy to get this from the data list
fig.savefig('two_plots.png')
You can also create two figures and plot into each of them explicitly, if you need them in different files for page layout, etc:
import matplotlib.pyplot as plt
from numpy.random import random
fig1, ax1 = plt.subplots(1)
fig2, ax2 = plt.subplots(1)
for c in 'abc': # or, for filename in [file-list]:
x, y = random((2, 5))
ax1.plot(x, y, label=c)
ax2.plot(x, y**2, label=c)
ax1.legend()
ax2.legend()
fig1.savefig('two_plots_1.png')
fig2.savefig('two_plots_2.png')
I am trying to set the format to two decimal numbers in a matplotlib subplot environment. Unfortunately, I do not have any idea how to solve this task.
To prevent using scientific notation on the y-axis I used ScalarFormatter(useOffset=False) as you can see in my snippet below. I think my task should be solved by passing further options/arguments to the used formatter. However, I could not find any hint in matplotlib's documentation.
How can I set two decimal digits or none (both cases are needed)? I am not able to provide sample data, unfortunately.
-- SNIPPET --
f, axarr = plt.subplots(3, sharex=True)
data = conv_air
x = range(0, len(data))
axarr[0].scatter(x, data)
axarr[0].set_ylabel('$T_\mathrm{air,2,2}$', size=FONT_SIZE)
axarr[0].yaxis.set_major_locator(MaxNLocator(5))
axarr[0].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[0].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[0].grid(which='major', alpha=0.5)
axarr[0].grid(which='minor', alpha=0.2)
data = conv_dryer
x = range(0, len(data))
axarr[1].scatter(x, data)
axarr[1].set_ylabel('$T_\mathrm{dryer,2,2}$', size=FONT_SIZE)
axarr[1].yaxis.set_major_locator(MaxNLocator(5))
axarr[1].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[1].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[1].grid(which='major', alpha=0.5)
axarr[1].grid(which='minor', alpha=0.2)
data = conv_lambda
x = range(0, len(data))
axarr[2].scatter(x, data)
axarr[2].set_xlabel('Iterationsschritte', size=FONT_SIZE)
axarr[2].xaxis.set_major_locator(MaxNLocator(integer=True))
axarr[2].set_ylabel('$\lambda$', size=FONT_SIZE)
axarr[2].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[2].yaxis.set_major_locator(MaxNLocator(5))
axarr[2].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[2].grid(which='major', alpha=0.5)
axarr[2].grid(which='minor', alpha=0.2)
See the relevant documentation in general and specifically
from matplotlib.ticker import FormatStrFormatter
fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))
If you are directly working with matplotlib's pyplot (plt) and if you are more familiar with the new-style format string, you can try this:
from matplotlib.ticker import StrMethodFormatter
plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.0f}')) # No decimal places
plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.2f}')) # 2 decimal places
From the documentation:
class matplotlib.ticker.StrMethodFormatter(fmt)
Use a new-style format string (as used by str.format()) to format the
tick.
The field used for the value must be labeled x and the field used for
the position must be labeled pos.
The answer above is probably the correct way to do it, but didn't work for me.
The hacky way that solved it for me was the following:
ax = <whatever your plot is>
# get the current labels
labels = [item.get_text() for item in ax.get_xticklabels()]
# Beat them into submission and set them back again
ax.set_xticklabels([str(round(float(label), 2)) for label in labels])
# Show the plot, and go home to family
plt.show()
format labels using lambda function
3x the same plot with differnt y-labeling
Minimal example
import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt
from matplotlib.ticker import FormatStrFormatter
fig, axs = mpl.pylab.subplots(1, 3)
xs = np.arange(10)
ys = 1 + xs ** 2 * 1e-3
axs[0].set_title('default y-labeling')
axs[0].scatter(xs, ys)
axs[1].set_title('custom y-labeling')
axs[1].scatter(xs, ys)
axs[2].set_title('x, pos arguments')
axs[2].scatter(xs, ys)
fmt = lambda x, pos: '1+ {:.0f}e-3'.format((x-1)*1e3, pos)
axs[1].yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fmt))
fmt = lambda x, pos: 'x={:f}\npos={:f}'.format(x, pos)
axs[2].yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fmt))
You can also use 'real'-functions instead of lambdas, of course.
https://matplotlib.org/3.1.1/gallery/ticks_and_spines/tick-formatters.html
In matplotlib 3.1, you can also use ticklabel_format. To prevents scientific notation without offsets:
plt.gca().ticklabel_format(axis='both', style='plain', useOffset=False)
How can I change the format of the numbers in the x-axis to be like 10,000 instead of 10000?
Ideally, I would just like to do something like this:
x = format((10000.21, 22000.32, 10120.54), "#,###")
Here is the code:
import matplotlib.pyplot as plt
# create figure instance
fig1 = plt.figure(1)
fig1.set_figheight(15)
fig1.set_figwidth(20)
ax = fig1.add_subplot(2,1,1)
x = 10000.21, 22000.32, 10120.54
y = 1, 4, 15
ax.plot(x, y)
ax2 = fig1.add_subplot(2,1,2)
x2 = 10434, 24444, 31234
y2 = 1, 4, 9
ax2.plot(x2, y2)
fig1.show()
Use , as format specifier:
>>> format(10000.21, ',')
'10,000.21'
Alternatively you can also use str.format instead of format:
>>> '{:,}'.format(10000.21)
'10,000.21'
With matplotlib.ticker.FuncFormatter:
...
ax.get_xaxis().set_major_formatter(
matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))
ax2.get_xaxis().set_major_formatter(
matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))
fig1.show()
The best way I've found to do this is with StrMethodFormatter:
import matplotlib as mpl
ax.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.0f}'))
For example:
import pandas as pd
import requests
import matplotlib.pyplot as plt
import matplotlib as mpl
url = 'https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USDT&aggregate=1'
df = pd.DataFrame({'BTC/USD': [d['close'] for d in requests.get(url).json()['Data']]})
ax = df.plot()
ax.yaxis.set_major_formatter(mpl.ticker.StrMethodFormatter('{x:,.0f}'))
plt.show()
I always find myself on this same page everytime I try to do this. Sure, the other answers get the job done, but aren't easy to remember for next time! ex: import ticker and use lambda, custom def, etc.
Here's a simple solution if you have an axes named ax:
ax.set_yticklabels(['{:,}'.format(int(x)) for x in ax.get_yticks().tolist()])
Short answer without importing matplotlib as mpl
plt.gca().yaxis.set_major_formatter(plt.matplotlib.ticker.StrMethodFormatter('{x:,.0f}'))
Modified from #AlexG's answer
If you like it hacky and short you can also just update the labels
def update_xlabels(ax):
xlabels = [format(label, ',.0f') for label in ax.get_xticks()]
ax.set_xticklabels(xlabels)
update_xlabels(ax)
update_xlabels(ax2)
You can use matplotlib.ticker.funcformatter
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tkr
def func(x, pos): # formatter function takes tick label and tick position
s = '%d' % x
groups = []
while s and s[-1].isdigit():
groups.append(s[-3:])
s = s[:-3]
return s + ','.join(reversed(groups))
y_format = tkr.FuncFormatter(func) # make formatter
x = np.linspace(0,10,501)
y = 1000000*np.sin(x)
ax = plt.subplot(111)
ax.plot(x,y)
ax.yaxis.set_major_formatter(y_format) # set formatter to needed axis
plt.show()
x = [10000.21, 22000.32, 10120.54]
You could use a list comprehension to make a list of labels, and then pass them the plt.xticks.
xlabels = [f'{label:,}' for label in x]
plt.xticks(x, xlabels)
If you want original values to appear in ticks, use
plt.xticks(ticks=plt.xticks()[0], labels=plt.xticks()[0])
This will prevent abbreviations like from 3000000 to 1.3 e5 etc. and will show 3000000 (the exact value) in ticks.
Easiest way in my opinion:
current_values = plt.gca().get_yticks()
plt.gca().set_yticklabels(['{:,.0f}'.format(x) for x in current_values])
From:
https://queirozf.com/entries/matplotlib-examples-number-formatting-for-axes-labels
For non-comma separators, improving the accepted answer, below answer will use SEP as the separator.
SEP = '.'
ax.get_yaxis().set_major_formatter(
matplotlib.ticker.FuncFormatter(
lambda x, p: str.replace(format(int(x), ','), ',', SEP)
)
)