I am unable to render correctly a matplotlib plot that contains hatch pattern to a pdf via reportlab.
Here is below a sample code that highlights the problem. The example is a simple but reportlab is needed in the project I am working on, so I cannot output straight to pdf from matplotlib.
from io import BytesIO
import matplotlib.pyplot as plt
import numpy as np
from reportlab.graphics import renderPDF
from reportlab.pdfgen import canvas
from svglib.svglib import svg2rlg
x = np.arange(100)
y = np.random.randint(50, 90, 100)
fig = plt.figure()
plt.plot(x, y, "o", color="b")
plt.axhspan(ymin=40, ymax=55, hatch="/", facecolor="none")
plt.show()
imgdata = BytesIO()
fig.savefig(imgdata, format="svg")
imgdata.seek(0)
drawing = svg2rlg(imgdata)
drawing.scale(0.5, 0.5)
c = canvas.Canvas("test.pdf")
renderPDF.draw(drawing, c, 10, 200)
c.drawString(100, 100, "No hatch pattern rendered")
c.showPage()
c.save()
The expected plot output is (as it is from matplotlib):
however, when exporting to pdf, I'm getting:
The culprit here seems to be svg2rlg that does not the following from the svg output of matplotlib:
Can't handle color: url(#h06c0184332)
Is there a way to circumvent this problem while keeping the output from matplotlib as svg?
I have a simple program to grab an image and plot its blue and green pixel histogram. I get the plot but I want to do some data science on the plots. Is there an easy way to convert the plot into a table either that I can copy and paste from or straight to a .csv?
import tkinter as tk
from tkinter import filedialog
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread(file_path)
color = ('b','g')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()
cv2.imread() returns a numpy array which you can save to a file, by default in binary format. To get a CSV formatted text file, use the sep parameter:
img.tofile('image.csv', sep=',')
See the official documentation for more information.
I am trying to create a simple DICOM viewer, in which I plot the image using matplotlib and I want to show that same plot(which is a DICOM image) in tkinter, but when I run the code I get this error. please help. The error occurs when I try to plot a, but I believe it has something to do wuth the way I declared the values of x, y, and p
import pydicom
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
from matplotlib.backends.backend_tkagg import
FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
from pydicom.data import get_testdata_files
filename = get_testdata_files('000000.dcm')
dataset = pydicom.dcmread('000000.dcm')
data = dataset.pixel_array
class mclass:
def __init__(self, window):
self.window = window
self.button=Button(window,text="check",command=self.plot)
self.button.pack()
def plot (self):
if 'PixelData' in dataset:
rows = int(dataset.Rows)
cols = int(dataset.Columns)
y=np.array(rows)
x=np.array(cols)
p=np.array(data)
fig = Figure(figsize=(6,6))
a = fig.add_subplot(111)
a.plot(p, range(2+max(y)))
canvas = FigureCanvasTkAgg(fig, master=self.window)
canvas.get_tk_widget().pack()
canvas.draw()
window = Tk()
start = mclass (window)
window.mainloop()
From the look of it your error lies here :
y=np.array(rows)
...
a.plot(p, range(2+max(y)))
You ask for the max(y), but the ds.Rows and ds.Columns you use to instantiate x and y are scalar values (and to be doubly sure you use int(ds.Rows)). This means that both x and y will be a 0-dimensional array and this would explain the thrown error, presumably on max(y). Try :
if 'PixelData' in dataset:
rows = int(dataset.Rows)
cols = int(dataset.Columns)
y=rows
x=cols
p=np.array(data)
fig = Figure(figsize=(6,6))
a = fig.add_subplot(111)
a.plot(p, range(2+y))
For some reason the y-tick and y-tick labels aren't showing up on my plot. The variable data is a pandas dataframe: rfr_scatter = pd.DataFrame({'Actual':y_test, 'Model Predicted':rfr_predictions})
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import tkinter as tk
from tkinter import *
def ScatterPlotter(notebooktab, data, test, pred):
f = Figure(figsize=(7,5), dpi=100)
ax1 = f.add_subplot(111, title="Model Performance")
for item in ([ax1.title, ax1.xaxis.label, ax1.yaxis.label] +
ax1.get_xticklabels() + ax1.get_yticklabels()):
item.set_fontsize(8)
item.set_color('black')
markersize = 0.8
alpha = 0.05
line = np.arange(min(test), min(test) + 35, 5)
data.plot.scatter(x='Actual', y='Model Predicted', ax=ax1, s=markersize, alpha=alpha)
ax1.set_xlim((min(test),max(test)))
ax1.set_ylim((pred.min(),pred.max()))
ax1.plot(line,line,clr_red,'--', label = "Perfect")
canvas = FigureCanvasTkAgg(f, notebooktab)
canvas.show()
canvas.get_tk_widget().pack()
And i get this:
I have tried setting the yticks to visible, with no luck. I'm probably missing something simple...
EDIT: removing ax1.set_ylim((pred.min(),pred.max())) gives me a couple marks on the graph, it almost looks like the label is over the text, or the text isn't finishing rendering.
Changing ax1.plot(line,line,clr_red,'--', label = "Perfect") to ax1.plot(line,line,'r--', label = "Perfect") fixed the problem
I am really struggling with matplotlib, escpecially with the axis settings. My goal is to set up 6 subplots in one figure, which all display different datasets but have the same amount of ticklabels.
The relevant part of my sourcecode looks like:
graph4.py:
# Import Matolotlib Modules #
import matplotlib as mpl
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
from matplotlib import ticker
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif']='Arial' #set font to arial
# Import GTK Modules #
import gtk
#Import System Modules #
import sys
# Import Numpy Modules #
from numpy import genfromtxt
import numpy
# Import Own Modules #
import mysubplot as mysp
class graph4():
weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
def __init__(self, graphview):
#create new Figure
self.figure = Figure(figsize=(100,100), dpi=75)
#create six subplots within self.figure
self.subplot = []
for j in range(6):
self.subplot.append(self.figure.add_subplot(321 + j))
self.__conf_subplots__() #configure title, xlabel, ylabel and grid of all subplots
#to make it look better
self.figure.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.96, wspace=0.2, hspace=0.6)
#Matplotlib <-> GTK
self.canvas = FigureCanvas(self.figure) # a gtk.DrawingArea
self.canvas.set_flags(gtk.HAS_FOCUS|gtk.CAN_FOCUS)
self.canvas.grab_focus()
self.canvas.show()
graphview.pack_start(self.canvas, True, True)
#add labels and grid to all subplots
def __conf_subplots__(self):
index = 0
for i in self.subplot:
mysp.conf_subplot(i, 'Zeit', 'Menge', graph4.weekdays[index], True)
i.plot([], [], 'bo') #empty plot
index +=1
def plot(self, filename_list):
index = 0
for filename in filename_list:
data = genfromtxt(filename, delimiter=',') #load data from filename
if data.size != 0: #only if file isn't empty
if index <= len(self.subplot): #plot every file on a different subplot
mysp.plot(self.subplot[index],data[0:, 1], data[0:, 0])
index +=1
self.canvas.draw()
def clear_plot(self):
#clear axis of all subplots
for i in self.subplot:
i.cla()
self.__conf_subplots__()
mysubplot.py: (helper module)
# Import Matplotlib Modules
from matplotlib.axes import Subplot
import matplotlib.dates as md
import matplotlib.pyplot as plt
# Import Own Modules #
import mytime as myt
# Import Numpy Modules #
import numpy as np
def conf_subplot(subplot, xlabel, ylabel, title, grid):
if(xlabel != None):
subplot.set_xlabel(xlabel)
if(ylabel != None):
subplot.set_ylabel(ylabel)
if(title != None):
subplot.set_title(title)
subplot.grid(grid)
#rotate xaxis labels
plt.setp(subplot.get_xticklabels(), rotation=30, fontsize=12)
#display date on xaxis
subplot.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
subplot.xaxis_date()
def plot(subplot, x, y):
subplot.plot(x, y, 'bo')
I think the best way to explain what goes wrong is with the use of screenshots. After I start my application, everything looks good:
If I double click a 'Week'-entry on the left, the method clear_plot() in graph4.py is called to reset all subplots. Then a list of filenames is passed to the method plot() in graph4.py. The method plot() opens each file and plots each dataset on a different subplot. So after I double click a entry, it looks like:
As you can see, each subplot has a different number of xtick labels, which looks pretty ugly to me. Therefore, I am looking for a solution to improve this. My first approach was to set the ticklabels manually with xaxis.set_ticklabels(), so that each subplot has the same number of ticklabels. However, as strange as it sounds, this only works on some datasets and I really don't know why. On some datasets, everything works fine and on other datasets, matplotlib is basically doing what it wants and displays xaxis labels that I didn't specify. I also tried FixedLocator(), but I got the same result. On some datasets it is working and on others, matplotlib is using a different number of xtick labels.
What am I doing wrong?
Edit:
As #sgpc suggested, I tried to use pyplot. My sourcecode now looks like this:
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
import matplotlib.dates as md
mpl.rcParams['font.sans-serif']='Arial' #set font to arial
import gtk
import sys
# Import Numpy Modules #
from numpy import genfromtxt
import numpy
# Import Own Modules #
import mysubplot as mysp
class graph2():
weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
def __init__(self, graphview):
self.figure, temp = plt.subplots(ncols=2, nrows=3, sharex = True)
#2d array -> list
self.axes = [ y for x in temp for y in x]
#axis: date
for i in self.axes:
i.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
i.xaxis_date()
#make space and rotate xtick labels
self.figure.autofmt_xdate()
#Matplotlib <-> GTK
self.canvas = FigureCanvas(self.figure) # a gtk.DrawingArea
self.canvas.set_flags(gtk.HAS_FOCUS|gtk.CAN_FOCUS)
self.canvas.grab_focus()
self.canvas.show()
graphview.pack_start(self.canvas, True, True)
def plot(self, filename_list):
index = 0
for filename in filename_list:
data = genfromtxt(filename, delimiter=',') #get dataset
if data.size != 0: #only if file isn't empty
if index < len(self.axes): #print each dataset on a different subplot
self.axes[index].plot(data[0:, 1], data[0:, 0], 'bo')
index +=1
self.canvas.draw()
#not yet implemented
def clear_plot(self):
pass
If I plot some datasets, I get the following output:
http://i.imgur.com/3ngYTNr.png (sorry, I still don't have enough reputation to embedd pictures)
Moreover, I am not really sure if sharing the x-axis is a really good idea, because it is possible that the x-values differ in every subplot (for example: in the first subplot, the x-values ranges from 8:00am - 11:00am and in the second subplot the x-values ranges from 7:00pm - 9:00pm).
If I get rid of sharex = True, I get the following output:
http://i.imgur.com/rxHeSyJ.png (sorry, I still don't have enough reputation to embedd pictures)
As you can see, the output now looks better. However now, the labels on the x-axes are not updated. I assume that is because the last suplots are empty.
My next attempt was to use an axis for each subplot. Therefore, I made this changes:
for i in self.axes:
plt.setp(i.get_xticklabels(), visible=True, rotation = 30) #<-- I added this line...
i.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
i.xaxis_date()
#self.figure.autofmt_xdate() #<--changed this line
self.figure.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.96, wspace=0.2, hspace=0.6) #<-- and added this line
Now I get the following output:
i.imgur.com/TmA1goE.png (sorry, I still don't have enough reputation to embedd pictures)
So with this attempt, I am basically struggling with the same problem as with Figure() and add_subplot().
I really don't know, what else I could try to make it work...
I would strongly recommend you to use pyplot.subplots() with sharex=True:
fig, axes = subplots(ncols=2, nrows=3, sharex= True)
Then you access each axes using:
ax = axes[i,j]
And you can plot doing:
ax.plot(...)
To control the number of ticks for each AxesSubplot you can use:
ax.locator_params(axis='x', nbins=6)
OBS: axis can be 'x', 'y' or 'both'