abarbu / matplotlib-haskell

Haskell bindings for Python's Matplotlib
Other
85 stars 12 forks source link

Adding plot style sheet #20

Open umeshu opened 8 months ago

umeshu commented 8 months ago

Since matplotlib offers easy plot customization using style sheets (https://matplotlib.org/stable/gallery/style_sheets/style_sheets_reference.html), I am looking for a way to include that in matplotlib-haskell.

I tried to use Combinator # e.g. mp # "matplotlib.pyplot.style.use('ggplot')", but when executed, the style was ignored although no error was reported.

So I'm wondering if anyone could point out a direction on how I could solve it.

Thank you.

abarbu commented 8 months ago

Hey! Can you give me a runnable example? Makes my life a lot easier to figure out what's wrong.

If you want to debug this, the simple solution is to output to a python file instead of running the code. Then you can look at it and see why that statement is being lost. The generated python is quite clean and readable.

umeshu commented 7 months ago

Thanks @abarbu for the response.

Sure, here is the example:

{-# LANGUAGE ExtendedDefaultRules #-}

import           Graphics.Matplotlib

xs :: [Double]
xs = [1, 2, 3, 4, 5, 6]

ys :: [Double]
ys = [1, 3, 2, 5, 2, 8]

main :: IO ()
main = do
  let mpStyle =  mp # "matplotlib.pyplot.style.use('ggplot')"
  let myPlot = plot xs ys @@ [o1 "bo-", o2 "linewidth" 1.5]
  let xLabel =  xlabel "$x$"
  let yLabel =  ylabel "$y$"
  let toRender =  mpStyle <> myPlot <> xLabel <> yLabel
  onscreen toRender
  pythonCode <- code toRender
  writeFile "testPlotStyle.py" pythonCode

The generated python file is shown below:

import matplotlib
matplotlib.use('agg')
import matplotlib.path as mpath
import matplotlib.patches as mpatches
import matplotlib.pyplot as plot
import matplotlib.cm as cm
import matplotlib.colors as mcolors
import matplotlib.collections as mcollections
import matplotlib.ticker as mticker
import matplotlib.image as mpimg
from mpl_toolkits.mplot3d import axes3d
import numpy as np
from scipy import interpolate
import os
import io
import sys
import json
import random, datetime
from matplotlib.dates import DateFormatter, WeekdayLocator
plot.rcParams['pcolor.shading'] ='auto'
fig = plot.gcf()
axes = [plot.gca()]
ax = axes[0]
matplotlib.pyplot.style.use('ggplot')
data = json.loads(open('/tmp/data535825-25.json').read())
p = ax.plot(data[0], data[1],'bo-',linewidth=1.5)
ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$y$')
plot.draw()
plot.show()

The problem is that matplotlib.pyplot.style.use('ggplot') should be placed before fig = plot.gcf() for python to process that; if there is a way to correct the placement, the issue is solved

abarbu commented 7 months ago

Ah, yeah, I should add clearer support for this use case in the future.

But the simple solution is to write your own onscreen and code functions. Then you can customize the preamble added to each plot. https://github.com/abarbu/matplotlib-haskell/blob/f85bb16bffc746d62b22c1e30c9c75501af5d81c/src/Graphics/Matplotlib.hs#L82

They're just 1 line each. pyIncludes takes as input one argument, the backend, but it's really just a placeholder for inserting arbitrary code very early in the process. You can insert "matplotlib.pyplot.style.use('ggplot')" right after the agg backend.

https://github.com/abarbu/matplotlib-haskell/blob/f85bb16bffc746d62b22c1e30c9c75501af5d81c/src/Graphics/Matplotlib/Internal.hs#L332