matplotlib 0.4.0 → 0.4.1
raw patch · 5 files changed
+468/−53 lines, 5 filesdep +adPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: ad
API changes (from Hackage documentation)
- Graphics.Matplotlib: xLabel :: String -> Matplotlib
- Graphics.Matplotlib: yLabel :: String -> Matplotlib
- Graphics.Matplotlib: zLabel :: String -> Matplotlib
- Graphics.Matplotlib.Internal: instance GHC.Classes.Eq Graphics.Matplotlib.Internal.MplotCommand
- Graphics.Matplotlib.Internal: instance GHC.Classes.Ord Graphics.Matplotlib.Internal.MplotCommand
- Graphics.Matplotlib.Internal: instance GHC.Show.Show Graphics.Matplotlib.Internal.MplotCommand
+ Graphics.Matplotlib: addAxes :: Matplotlib
+ Graphics.Matplotlib: annotate :: String -> Matplotlib
+ Graphics.Matplotlib: bar :: (ToJSON t1, ToJSON t) => t1 -> t -> Matplotlib
+ Graphics.Matplotlib: imshow :: MplotImage a => a -> Matplotlib
+ Graphics.Matplotlib: nonUniformImage :: (ToJSON t, ToJSON t1, ToJSON t2) => t2 -> t1 -> t -> Matplotlib
+ Graphics.Matplotlib: pcolor3 :: (ToJSON t, ToJSON t1, ToJSON t2) => t2 -> t1 -> t -> Matplotlib
+ Graphics.Matplotlib: pie :: (MplotValue val, ToJSON val) => val -> Matplotlib
+ Graphics.Matplotlib: quiver :: (ToJSON t, ToJSON t1, ToJSON t2, ToJSON t3, ToJSON t4) => t4 -> t3 -> t2 -> t1 -> Maybe t -> Matplotlib
+ Graphics.Matplotlib: quiverKey :: (MplotValue val3, MplotValue val2, MplotValue val1, MplotValue val) => val3 -> val2 -> val1 -> val -> Matplotlib
+ Graphics.Matplotlib: rc :: String -> Matplotlib
+ Graphics.Matplotlib: readImage :: MplotImage i => i -> Matplotlib
+ Graphics.Matplotlib: streamplot :: (ToJSON t, ToJSON t1, ToJSON t2, ToJSON t3) => t3 -> t2 -> t1 -> t -> Matplotlib
+ Graphics.Matplotlib: xlabel :: String -> Matplotlib
+ Graphics.Matplotlib: ylabel :: String -> Matplotlib
+ Graphics.Matplotlib: zlabel :: String -> Matplotlib
+ Graphics.Matplotlib.Internal: LoadImage :: x -> MplotCommand
+ Graphics.Matplotlib.Internal: class MplotImage a
+ Graphics.Matplotlib.Internal: instance Data.Aeson.Types.ToJSON.ToJSON a => Graphics.Matplotlib.Internal.MplotImage [[a]]
+ Graphics.Matplotlib.Internal: instance Graphics.Matplotlib.Internal.MplotImage GHC.Base.String
+ Graphics.Matplotlib.Internal: instance Graphics.Matplotlib.Internal.MplotValue [[GHC.Types.Double]]
+ Graphics.Matplotlib.Internal: loadPythonImage :: MplotImage a => a -> String -> FilePath -> String
+ Graphics.Matplotlib.Internal: pyReadImage :: [Char] -> [[Char]]
+ Graphics.Matplotlib.Internal: readImage :: MplotImage i => i -> Matplotlib
+ Graphics.Matplotlib.Internal: saveHaskellImage :: MplotImage a => a -> FilePath -> IO String
- Graphics.Matplotlib: figure :: MplotValue val => val -> Matplotlib
+ Graphics.Matplotlib: figure :: Matplotlib
Files
- README.md +120/−1
- matplotlib.cabal +2/−1
- src/Graphics/Matplotlib.hs +57/−10
- src/Graphics/Matplotlib/Internal.hs +42/−6
- test/Spec.hs +247/−35
README.md view
@@ -1,6 +1,6 @@ # Matplotlib -[](circleci.com/gh/abarbu/matplotlib-haskell)+[](https://circleci.com/gh/abarbu/matplotlib-haskell) [](https://hackage.haskell.org/package/matplotlib) Haskell bindings to Python's Matplotlib. It's high time that Haskell had a@@ -37,3 +37,122 @@ ```bash cabal install matplotlib ```++### Examples++Click on any of the examples below to go to the corresponding test that+generates it. Depending on your matplotlib version default colors might be+different.++[![integral][img_integral]][url_integral]+[![griddata][img_griddata]][url_griddata]+[![streamplot][img_streamplot]][url_streamplot]+[![hist2DLog][img_hist2DLog]][url_hist2DLog]+[![quadratic][img_quadratic]][url_quadratic]+[![spines][img_spines]][url_spines]+[![annotation][img_annotation]][url_annotation]+[![corr][img_corr]][url_corr]+[![bivariateNormal][img_bivariateNormal]][url_bivariateNormal]+[![images][img_images]][url_images]+[![labelled-histogram][img_labelled-histogram]][url_labelled-histogram]+[![projections][img_projections]][url_projections]+[![histogram][img_histogram]][url_histogram]+[![pcolorlog][img_pcolorlog]][url_pcolorlog]+[![scatter][img_scatter]][url_scatter]+[![stacked][img_stacked]][url_stacked]+[![legend][img_legend]][url_legend]+[![errorbar][img_errorbar]][url_errorbar]+[![line-options][img_line-options]][url_line-options]+[![quiver-fancy][img_quiver-fancy]][url_quiver-fancy]+[![contour][img_contour]][url_contour]+[![boxplot][img_boxplot]][url_boxplot]+[![show-matrix][img_show-matrix]][url_show-matrix]+[![scatterhist][img_scatterhist]][url_scatterhist]+[![hinton][img_hinton]][url_hinton]+[![density][img_density]][url_density]+[![violinplot][img_violinplot]][url_violinplot]+[![histMulti][img_histMulti]][url_histMulti]+[![cumulative][img_cumulative]][url_cumulative]+[![polar][img_polar]][url_polar]+[![hists][img_hists]][url_hists]+[![tex][img_tex]][url_tex]+[![eventplot][img_eventplot]][url_eventplot]+[![line-function][img_line-function]][url_line-function]+[![density-bandwidth][img_density-bandwidth]][url_density-bandwidth]+[![quiver][img_quiver]][url_quiver]+[![pie][img_pie]][url_pie]++[img_pcolorlog]: https://i.imgur.com/O8hU7Rl.png "pcolorlog"+[url_pcolorlog]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L495+[img_griddata]: https://i.imgur.com/Lh2xOPf.png "griddata"+[url_griddata]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L602+[img_streamplot]: https://i.imgur.com/AveIggT.png "streamplot"+[url_streamplot]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L582+[img_hist2DLog]: https://i.imgur.com/PnPyJzO.png "hist2DLog"+[url_hist2DLog]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L275+[img_cumulative]: https://i.imgur.com/W9JinZC.png "cumulative"+[url_cumulative]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L217+[img_eventplot]: https://i.imgur.com/2chP6qg.png "eventplot"+[url_eventplot]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L280+[img_quiver]: https://i.imgur.com/7G7r9gu.png "quiver"+[url_quiver]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L423+[img_labelled-histogram]: https://i.imgur.com/cOCIlpy.png "labelled-histogram"+[url_labelled-histogram]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L226+[img_line-options]: https://i.imgur.com/0Jr83hl.png "line-options"+[url_line-options]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L249+[img_violinplot]: https://i.imgur.com/MaWL0BD.png "violinplot"+[url_violinplot]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L298+[img_density]: https://i.imgur.com/v7GoIno.png "density"+[url_density]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L241+[img_tex]: https://i.imgur.com/liZci2C.png "tex"+[url_tex]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L259+[img_scatterhist]: https://i.imgur.com/DG3obq8.png "scatterhist"+[url_scatterhist]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L305+[img_boxplot]: https://i.imgur.com/aaoMEql.png "boxplot"+[url_boxplot]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L292+[img_histogram]: https://i.imgur.com/yO6dxEO.png "histogram"+[url_histogram]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L214+[img_images]: https://i.imgur.com/3ZqQDxR.png "images"+[url_images]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L482+[img_polar]: https://i.imgur.com/BTXgvqa.png "polar"+[url_polar]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L450+[img_histMulti]: https://i.imgur.com/hO2CFGQ.png "histMulti"+[url_histMulti]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L334+[img_annotation]: https://i.imgur.com/cPSB8BX.png "annotation"+[url_annotation]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L539+[img_corr]: https://i.imgur.com/fh4Bv2X.png "corr"+[url_corr]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L254+[img_scatter]: https://i.imgur.com/qg7W3oc.png "scatter"+[url_scatter]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L219+[img_hinton]: https://i.imgur.com/RPkTFIJ.png "hinton"+[url_hinton]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L380+[img_quiver-fancy]: https://i.imgur.com/VQ5RrfJ.png "quiver-fancy"+[url_quiver-fancy]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L434+[img_quadratic]: https://i.imgur.com/fs96snF.png "quadratic"+[url_quadratic]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L245+[img_line-function]: https://i.imgur.com/5REiBVO.png "line-function"+[url_line-function]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L243+[img_integral]: https://i.imgur.com/OxGr14f.png "integral"+[url_integral]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L396+[img_contour]: https://i.imgur.com/kMxGGot.png "contour"+[url_contour]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L224+[img_projections]: https://i.imgur.com/laubT7H.png "projections"+[url_projections]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L247+[img_legend]: https://i.imgur.com/EmpXjUY.png "legend"+[url_legend]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L270+[img_spines]: https://i.imgur.com/fc9xwh5.png "spines"+[url_spines]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L352+[img_density-bandwidth]: https://i.imgur.com/e48WAPS.png "density-bandwidth"+[url_density-bandwidth]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L238+[img_bivariateNormal]: https://i.imgur.com/ngX1N2R.png "bivariateNormal"+[url_bivariateNormal]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L465+[img_pie]: https://i.imgur.com/DSRqr14.png "pie"+[url_pie]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L516+[img_show-matrix]: https://i.imgur.com/bd6x6Es.png "show-matrix"+[url_show-matrix]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L267+[img_hists]: https://i.imgur.com/hJaxZJt.png "hists"+[url_hists]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L371+[img_errorbar]: https://i.imgur.com/ooTcL40.png "errorbar"+[url_errorbar]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L287+[img_stacked]: https://i.imgur.com/TIqlAtR.png "stacked"+[url_stacked]: https://github.com/abarbu/matplotlib-haskell/blob/master/test/Spec.hs#L523
matplotlib.cabal view
@@ -1,5 +1,5 @@ name: matplotlib-version: 0.4.0+version: 0.4.1 synopsis: Bindings to Matplotlib; a Python plotting library description: Matplotlib is probably the most full featured plotting library out there.@@ -48,6 +48,7 @@ , random , raw-strings-qq , split+ , ad ghc-options: -threaded -rtsopts -with-rtsopts=-N default-language: Haskell2010
src/Graphics/Matplotlib.hs view
@@ -57,13 +57,14 @@ -- in "ax". Plotting commands should use the current axis, never the plot -- itself; the two APIs are almost identical. When creating low-level bindings -- one must remember to call "plot.sci" to set the current image when plotting a--- graph. The current spine of the axes that's being manipulated is in "spine".+-- graph. The current spine of the axes that's being manipulated is in+-- "spine". The current quiver is in "q" ----------------------------------------------------------------------------- module Graphics.Matplotlib ( module Graphics.Matplotlib -- * Creating custom plots and applying options- , Matplotlib(), Option(),(@@), (%), o1, o2, (##), (#), mp, def, readData+ , Matplotlib(), Option(),(@@), (%), o1, o2, (##), (#), mp, def, readData, readImage , str, raw, lit, updateAxes, updateFigure, mapLinear) where import Data.List@@ -89,6 +90,7 @@ -- | Plot the cross-correlation and autocorrelation of several variables. TODO Due to -- a limitation in the options mechanism this takes explicit options. xacorr xs ys opts = readData (xs, ys)+ % figure % addSubplot 2 1 1 % xcorr xs ys @@ opts % grid True@@ -111,6 +113,11 @@ scatter x y = readData (x, y) % mp # "plot.sci(ax.scatter(data[0], data[1]" ## "))" +-- | Create a bar at a position with a height+bar :: (ToJSON t1, ToJSON t) => t1 -> t -> Matplotlib+bar left height = readData (left, height)+ % mp # "ax.bar(data[0], data[1]" ## ")"+ -- | Plot a line line :: (ToJSON t1, ToJSON t) => t1 -> t -> Matplotlib line x y = plot x y `def` [o1 "-"]@@ -171,11 +178,29 @@ matShow d = readData d % (mp # "plot.sci(ax.matshow(data" ## "))") +-- | Plot an image+imshow :: MplotImage a => a -> Matplotlib+imshow i = readImage i+ % (mp # "plot.sci(ax.imshow(img" ## "))")+ -- | Plot a matrix pcolor :: ToJSON a => a -> Matplotlib pcolor d = readData d % (mp # "plot.sci(ax.pcolor(np.array(data)" ## "))") +-- | Plot a matrix+pcolor3 x y z = readData (x,y,z)+ % (mp # "plot.sci(ax.pcolor(np.array(data[0]),np.array(data[1]),np.array(data[2])" ## "))")++-- | Create a non-uniform image from samples+nonUniformImage x y z = readData (x,y,z)+ % mp # "im = mpimg.NonUniformImage(ax" ## ")"+ % mp # "im.set_data(data[0], data[1], data[2])"++-- | Create a pie chart+pie l = readData l+ % mp # "plot.pie(" # l ## ")"+ -- | Plot a KDE of the given functions; a good bandwith will be chosen automatically density :: [Double] -> Maybe (Double, Double) -> Matplotlib density l maybeStartEnd =@@ -186,6 +211,9 @@ -- * Matplotlib configuration +-- | Set an rc parameter+rc s = mp # "plot.rc(" # str s ## ")"+ -- | Set an rcParams key-value setParameter k v = mp # "matplotlib.rcParams["# str k #"] = " # v @@ -207,6 +235,9 @@ plot :: (ToJSON t, ToJSON t1) => t1 -> t -> Matplotlib plot x y = readData (x, y) % dataPlot 0 1 +streamplot x y u v = readData (x, y, u, v)+ % mp # "ax.streamplot(np.asarray(data[0]), np.asarray(data[1]), np.asarray(data[2]), np.asarray(data[3])" ## ")"+ -- | Plot x against y where x is a date. -- xunit is something like 'weeks', yearStart, monthStart, dayStart are an offset to x. -- TODO This isn't general enough; it's missing some settings about the format. The call is also a mess.@@ -310,11 +341,24 @@ -- | Plot auto-correlation acorr x = readData x % mp # "ax.acorr(data" ## ")" +-- | A quiver plot; color is optional and can be nothing+quiver x y u v Nothing = readData(x,y,u,v)+ % mp # "q = ax.quiver(data[0], data[1], data[2], data[3]" ## ")"+quiver x y u v (Just c) = readData(x,y,u,v,c)+ % mp # "q = ax.quiver(data[0], data[1], data[2], data[3], data[4]" ## ")"++-- | A key of a given size with a label for a quiver plot+quiverKey x y u label = mp # "ax.quiverkey(q, "#x#", "#y#", "#u#", "#label##")"+ -- | Plot text at a specified location text x y s = mp # "ax.text(" # x # "," # y # "," # raw s ## ")" +-- | Add a text to a figure instead of a particular plot figText x y s = mp # "plot.figtext(" # x # "," # y # "," # raw s ## ")" +-- | Add an annotation+annotate s = mp # "ax.annotate(" # str s ## ")"+ -- * Layout, axes, and legends -- | Square up the aspect ratio of a plot.@@ -386,16 +430,16 @@ % mp # "ax.set_zlim3d(" # minimum2 zs # ", " # maximum2 zs # ")" -- | Add a label to the x axis-xLabel :: String -> Matplotlib-xLabel label = mp # "ax.set_xlabel(" # raw label ## ")"+xlabel :: String -> Matplotlib+xlabel label = mp # "ax.set_xlabel(" # raw label ## ")" -- | Add a label to the y axis-yLabel :: String -> Matplotlib-yLabel label = mp # "ax.set_ylabel(" # raw label ## ")"+ylabel :: String -> Matplotlib+ylabel label = mp # "ax.set_ylabel(" # raw label ## ")" -- | Add a label to the z axis-zLabel :: String -> Matplotlib-zLabel label = mp # "ax.set_zlabel(" # raw label ## ")"+zlabel :: String -> Matplotlib+zlabel label = mp # "ax.set_zlabel(" # raw label ## ")" setSizeInches w h = mp # "fig.set_size_inches(" # w # "," # h # ", forward=True)" @@ -461,9 +505,12 @@ -- | Access a subplot setSubplot s = mp # "ax = axes[" # s # "]" % setAx --- | Add axes to a figure+-- | Add axes to a plot axes = mp # "ax = plot.axes(" ## ")" % updateAxes % setAx +-- | Add axes to a figure+addAxes = mp # "ax = fig.add_axes(" ## ")" % updateAxes % setAx+ -- | Creates a new figure with the given id. If the Id is already in use it -- switches to that figure.-figure id = mp # "plot.figure(" # id ## ")" % updateFigure+figure = mp # "plot.figure(" ## ")" % updateFigure
src/Graphics/Matplotlib/Internal.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, FlexibleContexts, ExtendedDefaultRules #-}+{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, FlexibleContexts, ExtendedDefaultRules, ExistentialQuantification #-} -- | -- Internal representations of the Matplotlib data. These are not API-stable -- and may change. You can easily extend the provided bindings without relying@@ -33,10 +33,10 @@ -- | A maplotlib command, right now we have a very shallow embedding essentially -- dealing in strings containing python code as well as the ability to load -- data. The loaded data should be a json object.-data MplotCommand- = LoadData B.ByteString+data MplotCommand =+ LoadData B.ByteString+ | forall x. MplotImage x => LoadImage x | Exec { es :: String }- deriving (Show, Eq, Ord) -- | Throughout the API we need to accept options in order to expose -- matplotlib's many configuration options.@@ -50,6 +50,7 @@ -- | Convert an 'MplotCommand' to python code, doesn't do much right now toPy :: MplotCommand -> String toPy (LoadData _) = error "withMplot needed to load data"+toPy (LoadImage _) = error "withMplot needed to load images" toPy (Exec str) = str -- | Resolve the pending command with no options provided.@@ -75,6 +76,12 @@ B.hPutStr dataHandle obj hClose dataHandle preload l $ ((map Exec $ pyReadData dataFile) ++ cmds))+ preload ((LoadImage img):l) cmds = do+ withSystemTempFile "data.json" $+ (\dataFile dataHandle -> do+ hClose dataHandle+ obj <- saveHaskellImage img dataFile+ preload l $ ([Exec $ "img = " ++ (loadPythonImage img obj dataFile)] ++ cmds)) preload (c:l) cmds = preload l (c:cmds) -- | Create a plot that executes the string as python code@@ -85,10 +92,14 @@ mp :: Matplotlib mp = Matplotlib S.empty Nothing S.empty --- | Load the given data into the 'data' array+-- | Load the given data into the python "data" array readData :: ToJSON a => a -> Matplotlib readData d = Matplotlib (S.singleton $ LoadData $ encode d) Nothing S.empty +-- | Load the given image into python "img" variable+readImage :: MplotImage i => i -> Matplotlib+readImage i = Matplotlib (S.singleton $ LoadImage i) Nothing S.empty+ infixl 5 % -- | Combine two matplotlib commands (%) :: Matplotlib -> Matplotlib -> Matplotlib@@ -187,9 +198,28 @@ instance MplotValue x => MplotValue (Maybe x) where toPython Nothing = "None" toPython (Just x) = toPython x+instance MplotValue [[Double]] where+ toPython s = "np.asarray([" ++ f s ++ "])"+ where f [] = ""+ f (x:xs) = toPython x ++ "," ++ f xs default (Integer, Int, Double) +-- | The class of Haskell images or references to imagese which can be+-- transferred to matplotlib.+class MplotImage a where+ saveHaskellImage :: a -> FilePath -> IO String+ loadPythonImage :: a -> String -> FilePath -> String++-- | An image that is a string is a file path.+instance MplotImage String where+ saveHaskellImage _ _ = return ""+ loadPythonImage s _ _ = "mpimg.imread('" ++ toPython s ++ "')"++instance ToJSON a => MplotImage [[a]] where+ saveHaskellImage d fp = (B.writeFile fp $ encode d) >> return ""+ loadPythonImage s _ fp = unlines $ pyReadData fp+ -- $ Options -- | Add an option to the last matplotlib command. Commands can have only one option!@@ -249,6 +279,7 @@ def :: Matplotlib -> [Option] -> Matplotlib def m os = optionFn (defFn os) m +-- | Merge two sets of options defFn :: [Option] -> [Option] -> [Option] defFn os os' = merge ps' ps ++ (nub $ ks' ++ ks) where isK (K _ _) = True@@ -285,10 +316,11 @@ ,"import matplotlib.patches as mpatches" ,"import matplotlib.pyplot as plot" ,"import matplotlib.mlab as mlab"+ ,"import matplotlib.cm as cm" ,"import matplotlib.colors as mcolors" ,"import matplotlib.collections as mcollections" ,"import matplotlib.ticker as mticker"- ,"from matplotlib import cm"+ ,"import matplotlib.image as mpimg" ,"from mpl_toolkits.mplot3d import axes3d" ,"import numpy as np" ,"import os"@@ -303,6 +335,10 @@ -- | The python command that reads external data into the python data array pyReadData :: [Char] -> [[Char]] pyReadData filename = ["data = json.loads(open('" ++ filename ++ "').read())"]++-- | The python command that reads an image into the img variable+pyReadImage :: [Char] -> [[Char]]+pyReadImage filename = ["img = mpimg.imread('" ++ filename ++ "')"] -- | Detach python so we don't block (TODO This isn't working reliably) pyDetach :: [[Char]]
test/Spec.hs view
@@ -16,6 +16,7 @@ import qualified Data.ByteString as BS import System.Process import System.IO+import Numeric.AD -- * Random values for testing @@ -93,6 +94,11 @@ -- deviation). normals' (mean, sigma) g = map (\x -> x * sigma + mean) $ normals +pdfBivariateNormal x y sigmax sigmay mux muy sigmaxy =+ 1/(2*pi*sigmax*sigmay*(sqrt(1-rho^2)))*exp(-z/(2*(1-rho^2)))+ where rho = sigmaxy/(sigmax*sigmay)+ z = (x-mux)^2/sigmax^2-(2*rho*(x-mux)*(y-muy))/(sigmax*sigmay)+(y-muy)^2/sigmay^2+ -- * Tests main = defaultMain $ tests "All tests" testPlot@@ -153,17 +159,17 @@ file ("/tmp/imgs/" ++ name ++ ".png") fn basicTests f = testGroup "Basic tests"- [ f "histogram" m1- , f "cumulative" m2- , f "scatter" m3- , f "contour" m4- , f "labelled-histogram" m5- , f "density-bandwidth" m7- , f "density" m8- , f "line-function" m9- , f "quadratic" m10- , f "projections" m11- , f "line-options" m12+ [ f "histogram" mhistogram+ , f "cumulative" mcumulative+ , f "scatter" mscatter+ , f "contour" mcontour+ , f "labelled-histogram" mlabelledHistogram+ , f "density-bandwidth" mdensityBandwidth+ , f "density" mdensity+ , f "line-function" mlineFunction+ , f "quadratic" mQuadratic+ , f "projections" mProjections+ , f "line-options" mlineOptions , f "corr" mxcorr , f "show-matrix" mmat , f "legend" mlegend@@ -176,61 +182,72 @@ , f "hists" mhists , f "hinton" mhinton , f "integral" mintegral+ , f "quiver" mquiver+ , f "quiver-fancy" mquiverFancy+ , f "polar" mpolar+ , f "bivariateNormal" mbivariateNormal+ , f "images" mimages+ , f "pcolorlog" mpcolorlog+ , f "pie" mpie+ , f "stacked" mstacked+ , f "annotation" mannotation+ , f "streamplot" mstreamplot ] fragileTests f = testGroup "Fragile tests"- [ -- TODO Fails on circle ci (with latex)- f "tex" mtex+ [ f "tex" mtex -- TODO Fails on circle ci (with latex) -- TODO Fails on circle ci (labels is not valid; matplotlib too old) , f "boxplot" mboxplot -- TODO Fails on circle ci (no violin plots; matplotlib too old) , f "violinplot" mviolinplot+ -- TODO Needs a fairly recent matplotlib; too old for circleci+ , f "griddata" mgriddata ] failingTests f = testGroup "Failing tests" [ -- TODO This test case is broken- f "sub-bars" m6+ f "sub-bars" msubBars ] -- * These tests are fully-reproducible, the output must be identical every time -m1 :: Matplotlib-m1 = histogram xs 8+mhistogram :: Matplotlib+mhistogram = histogram xs 8 -m2 :: Matplotlib-m2 = histogram xs 8 @@ [o2 "cumulative" True]+mcumulative = histogram xs 8 @@ [o2 "cumulative" True] -m3 = scatter xs ys+mscatter = scatter xs ys degreesRadians a = a * pi / 180.0 radiansDegrees a = a * 180.0 / pi -m4 = contourF (\a b -> sin (degreesRadians a) + cos (degreesRadians b)) (-100) 100 (-200) 200 10+mcontour = contourF (\a b -> sin (degreesRadians a) + cos (degreesRadians b)) (-100) 100 (-200) 200 10 -m5 = histogram xs 7- % yLabel "number of queries"- % xLabel "true positives"+mlabelledHistogram = histogram xs 7+ % ylabel "number of queries"+ % xlabel "true positives" -m6 = subplotBarsLabelled+-- TODO Broken test+msubBars = subplotBarsLabelled [[40, 50, 20, 50], [10, 20, 30, 40], [40, 50, 20, 50]] ["a", "b", "c", "d"] [] % title "Wee a title"- % xLabel "X"- % yLabel "Y"+ % xlabel "X"+ % ylabel "Y" -m7 = densityBandwidth [2.1, 1.3, 0.4, 1.9, 5.1, 6.2] 1.5 (Just (-6, 11))+mdensityBandwidth = densityBandwidth [2.1, 1.3, 0.4, 1.9, 5.1, 6.2] 1.5 (Just (-6, 11)) % ylim 0 0.2 -m8 = density [2.1, 1.3, 0.4, 1.9, 5.1, 6.2] (Just (-6, 11))+mdensity = density [2.1, 1.3, 0.4, 1.9, 5.1, 6.2] (Just (-6, 11)) -m9 = lineF (\x -> x**2) [0,0.01..1]+mlineFunction = lineF (\x -> x**2) [0,0.01..1] -m10 = plotMapLinear (\x -> x**2) (-2) 2 100 @@ [o1 "."] % title "Quadratic function"+mQuadratic = plotMapLinear (\x -> x**2) (-2) 2 100 @@ [o1 "."] % title "Quadratic function" -m11 = projectionsF (\a b -> cos (degreesRadians a) + sin (degreesRadians b)) (-100) 100 (-200) 200 10+mProjections = projectionsF (\a b -> cos (degreesRadians a) + sin (degreesRadians b)) (-100) 100 (-200) 200 10 -m12 = plot [1,2,3,4,5,6] [1,3,2,5,2,4] @@ [o1 "go-", o2 "linewidth" 2]+mlineOptions = plot [1,2,3,4,5,6] [1,3,2,5,2,4] @@ [o1 "go-", o2 "linewidth" 2] -- * These tests can be random and may not be exactly the same every time @@ -243,8 +260,8 @@ mtex = plotMapLinear cos 0 1 100 % setTeX True % setUnicode True- % xLabel [r|\textbf{time (s)}|]- % yLabel [r|\textit{Velocity (\u00B0/sec)}|] @@ [o2 "fontsize" 16]+ % xlabel [r|\textbf{time (s)}|]+ % ylabel [r|\textit{Velocity (\u00B0/sec)}|] @@ [o2 "fontsize" 16] % title [r|\TeX\ is Number $\displaystyle\sum_{n=1}^\infty\frac{-e^{i\pi}}{2^n}$!"|] @@ [o2 "fontsize" 16, o2 "color" "r"] % grid True @@ -286,7 +303,7 @@ % violinplot (take 3 $ chunksOf 100 $ map (* 2) $ normals) @@ [o2 "showmeans" True, o2 "showmedians" True, o2 "vert" False] -- | http://matplotlib.org/examples/pylab_examples/scatter_hist.html-mscatterHist = figure 0+mscatterHist = figure @@ [o1 0] % setSizeInches 8 8 -- The scatter plot % axes @@ [o1 ([left, bottom', width, height] :: [Double])]@@ -403,3 +420,198 @@ -- shaded region (ix :: [Double]) = mapLinear (\x -> x) a b 100 iy = map func ix++mquiver = quiver x y u v (Nothing :: Maybe [Double]) @@ [o2 "units" "width"]+ % quiverKey 0.9 0.93 2 (raw [r|$2 \frac{m}{s}$|])+ @@ [o2 "labelpos" "E", o2 "coordinates" "figure"]+ % xlim (-0.2) 6.4+ % ylim (-0.2) 6.4+ where m = [ (x,y,cos x,sin y) | x <- [0,0.2..2*pi], y <- [0,0.2..2*pi] ]+ x = map (\(x,_,_,_) -> x) m+ y = map (\(_,x,_,_) -> x) m+ u = map (\(_,_,x,_) -> x) m+ v = map (\(_,_,_,x) -> x) m++mquiverFancy = quiver x y u v (Just mag) @@ [o2 "units" "x"+ ,o2 "pivot" "tip"+ ,o2 "width" 0.022+ ,o2 "scale" (1 / 0.15)]+ % quiverKey 0.9 0.93 1 (raw [r|$2 \frac{m}{s}$|])+ @@ [o2 "labelpos" "E", o2 "coordinates" "figure"]+ % scatter x y @@ [o2 "color" "k", o2 "s" 5]+ % xlim (-0.2) 6.4+ % ylim (-0.2) 6.4+ where m = [ (x,y,cos x,sin y) | x <- [0,0.2..2*pi], y <- [0,0.2..2*pi] ]+ x = map (\(x,_,_,_) -> x) m+ y = map (\(_,x,_,_) -> x) m+ u = map (\(_,_,x,_) -> x) m+ v = map (\(_,_,_,x) -> x) m+ mag = zipWith (\x x' -> sqrt(x**2 + x'**2)) u v++mpolar = rc "grid" @@ [o2 "color" "#316931", o2 "linewidth" 1, o2 "linestyle" "-"]+ % rc "xtick" @@ [o2 "labelsize" 15]+ % rc "ytick" @@ [o2 "labelsize" 15]+ % figure @@ [o2 "figsize" (8::Int,8::Int)]+ % addAxes @@ [o1 [0.1, 0.1, 0.8, 0.8::Double], o2 "projection" "polar"+ -- TODO My matplotlib doesn't seem to have this property+ -- , o2 "facecolor" "#d5de9c"+ ]+ % plot theta r @@ [o2 "color" "#ee8d18", o2 "lw" 3, o2 "label" "a line"]+ % plot (map (\x -> 0.5*x) theta) r+ @@ [o2 "color" "blue", o2 "ls" "--", o2 "lw" 3, o2 "label" "another line"]+ % legend+ where r = [0,0.01..3.0]+ theta = map (\x -> 2*pi*x) r++mbivariateNormal =+ imshow vs @@ [o2 "interpolation" "bilinear"+ ,o2 "cmap" $ raw "RdYlGn"+ ,o2 "origin" "lower"+ ,o2 "extent" [-3::Double, 3, -3, 3]+ ,o2 "vmin" $ (0-) $ maximum $ map abs vs'+ ,o2 "vmax" $ maximum $ map abs vs']+ where delta = 0.025::Double+ xs = [-3.0,-3.0+delta..3.0]+ ys = [-3.0,-3.0+delta..3.0]+ vs = [[pdfBivariateNormal x y 1.5 0.5 1.0 1.0 0.0+ - pdfBivariateNormal x y 1.0 1.0 0.0 0.0 0.0+ | x <- xs]+ | y <- ys]+ vs' = foldl' (++) [] vs++-- TODO This is subtly broken+mimages = -- figure @@ [o2 "figsize" (10::Int,10::Int)]+ -- %+ subplots @@ [o2 "nrows" 1, o2 "ncols" 2]+ % setSubplot 0+ % imshow "data/heightmap.png" @@ [o2 "interpolation" "nearest"]+ % setSubplot 1+ % mp # "ls = mcolors.LightSource(azdeg=315, altdeg=45)"+ % mp # "ax.imshow(ls.shade(img, cmap=cm.gist_earth))"+ -- TODO This doesn't work on my matplab version+ -- vert_exag=0.05,+ --, blend_mode='overlay'+ % xlabel "overlay blend mode"++mpcolorlog = figure+ % addSubplot 2 1 1+ % pcolor3 xs' ys' vs @@ [o2 "cmap" $ raw "PuBu_r"]+ % colorbar+ % addSubplot 2 1 2+ % pcolor3 xs' ys' vs @@ [o2 "norm"+ (lit $ "mcolors.LogNorm(vmin="++(show $ minimum vs')+++ ", vmax="++(show $ maximum vs')++")")+ ,o2 "cmap" $ raw "PuBu_r"]+ % colorbar+ where delta = 0.1::Double+ xs = [-3.0,-3.0+delta..3.0]+ ys = [-3.0,-3.0+delta..3.0]+ vs = [[pdfBivariateNormal x y 0.1 0.2 1.0 1.0 0.0+ + 0.1 * pdfBivariateNormal x y 1.0 1.0 0.0 0.0 0.0+ | x <- xs]+ | y <- ys]+ xs' = [[ x | x <- xs]| y <- ys]+ ys' = [[ y | x <- xs]| y <- ys]+ vs' = foldl' (++) [] vs++mpie = pie [15, 30, 45, 10 :: Double]+ @@ [o2 "explode" [0, 0.05, 0, 0 :: Double]+ ,o2 "labels" ["Frogs", "Hogs", "Dogs", "Logs"]+ ,o2 "autopct" "%.0f%%"+ ,o2 "shadow" True]++-- | http://matplotlib.org/examples/pylab_examples/bar_stacked.html+mstacked =+ -- TODO The locations of the bars is off+ bar [0..4] ms @@ [o1 width, o2 "color" "#d62728", o2 "yerr" mStd, o2 "label" "ms"]+ % bar [0..4] ws @@ [o1 width, o2 "bottom" ms, o2 "yerr" wStd, o2 "label" "ws"]+ % xticks [0..4 :: Int]+ % xtickLabels "['G1', 'G2', 'G3', 'G4', 'G5']"+ % title "Scores"+ % ylabel "Score"+ % yticks [0,10..80 :: Int]+ % legend+ where ms = [20 :: Double, 35, 30, 35, 27]+ ws = [25 :: Double, 32, 34, 20, 25]+ mStd = [2 :: Double, 3, 4, 1, 2]+ wStd = [3 :: Double, 5, 2, 3, 3]+ width = 0.35 :: Double++mannotation = -- figure @@ [o2 "figsize" (10::Int,10::Int)]+ -- TODO This is subtly broken+ -- TODO Dictionaries+ plot t s @@ [o2 "lw" 3]+ % xlim (-1) 5+ % ylim (-4) 3+ % annotate "straight" @@ [o2 "xy" [0, 1::Double], o2 "xycoords" "data", o2 "xytext" [-50, 30 :: Double]+ ,o2 "textcoords" "offset points", o2 "arrowprops" (lit "dict(arrowstyle='->')")]+ % annotate "arc3,\\nrad 0.2" @@ [o2 "xy" [0.5, -1::Double], o2 "xycoords" "data", o2 "xytext" [-80, -60 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='arc3,rad=.2')")]+ % annotate "arc,\\nangle 50" @@ [o2 "xy" [1, 1::Double], o2 "xycoords" "data", o2 "xytext" [-90, 50 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='arc,angleA=0,armA=50,rad=10')")]+ % annotate "arc,\\narms" @@ [o2 "xy" [1.5, -1::Double], o2 "xycoords" "data", o2 "xytext" [-80, -60 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='arc,angleA=0,armA=40,angleB=-90,armB=30,rad=7')")]+ % annotate "angle,\\nangle 90" @@ [o2 "xy" [2, 1::Double], o2 "xycoords" "data", o2 "xytext" [-70, 30 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='angle,angleA=0,angleB=90,rad=10')")]+ % annotate "angle3,\\nangle -90" @@ [o2 "xy" [2.5, -1::Double], o2 "xycoords" "data", o2 "xytext" [-80, -60 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='angle3,angleA=0,angleB=-90')")]+ % annotate "angle,\\nround" @@ [o2 "xy" [3, 1::Double], o2 "xycoords" "data", o2 "xytext" [-60, 30 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "bbox" (lit "dict(boxstyle='round', fc='0.8')")+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='angle,angleA=0,angleB=90,rad=10')")]+ % annotate "angle,\\nround4" @@ [o2 "xy" [3.5, -1::Double], o2 "xycoords" "data", o2 "xytext" [-70, -80 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "bbox" (lit "dict(boxstyle='round4,pad=.5', fc='0.8')")+ ,o2 "size" 20+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='angle,angleA=0,angleB=-90,rad=10')")]+ % annotate "angle,\\nshrink" @@ [o2 "xy" [4, 1::Double], o2 "xycoords" "data", o2 "xytext" [-60, 30 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "bbox" (lit "dict(boxstyle='round', fc='0.8')")+ ,o2 "arrowprops" (lit "dict(arrowstyle='->', connectionstyle='angle,angleA=0,angleB=90,rad=10')")]+ -- TODO This annotation doesn't render correctly on my matplotlib version+ % annotate "" @@ [o2 "xy" [4, 1::Double], o2 "xycoords" "data", o2 "xytext" [4.5, -1 :: Double]+ ,o2 "textcoords" "offset points"+ ,o2 "arrowprops" (lit "dict(arrowstyle='<->', connectionstyle='bar', ec='k', shrinkA=5, shrinkB=5)")]+ where t = [0, 0.01 .. 5.0 :: Double]+ s = map (\x -> cos $ 2*pi*x) t++mstreamplot = streamplot xs ys xs' ys' @@ [o2 "linewidth" mag']+ -- useful for seeing the energy landscape+ -- pcolor3 xmat ymat vs+ where delta = 0.05::Double+ xs = [-3.0,-3.0+delta..3.0]+ ys = [-3.0,-3.0+delta..3.0]+ xmat = [[ x | x <- xs]| y <- ys]+ ymat = [[ y | x <- xs]| y <- ys]+ ms = [[grad' (\[xv,yv] ->+ pdfBivariateNormal xv yv 0.4 0.7 1.0 1.0 0.0+ + pdfBivariateNormal xv yv 1.0 1.0 (-1.0) (-1.0) 0.0)+ [x,y]+ | x <- xs]+ | y <- ys] :: [[(Double, [Double])]]+ vs = map2 (\(v, _) -> v) ms+ xs' = map2 (\(_, [x, _]) -> x) ms+ ys' = map2 (\(_, [_, y]) -> y) ms+ mag' = zipWith (\lx ly -> zipWith (\x y -> 5 * (log $ 1 + (sqrt $ x*x + y*y))) lx ly) xs' ys'+ map2 f l = map (\r -> map f r) l++mgriddata = readData (x, y, z, xi, yi)+ -- TODO This requires a lot of manual indexing. Next big API change will be to+ -- have references to loaded data.+ % mp # "data.append(mlab.griddata(data[0], data[1], data[2], data[3], data[4], interp='linear'))"+ % mp # "plot.sci(ax.contour(data[3], data[4], data[5], 15, linewidths=0.5, colors='k'))"+ % mp # "plot.sci(ax.contourf(data[3], data[4], data[5], 15, vmax=abs(data[5]).max(), vmin=-abs(data[5]).max()))"+ % colorbar+ % scatter x y @@ [o2 "marker" "o", o2 "s" 5, o2 "zorder" 10]+ % xlim (-2) 2+ % ylim (-2) 2+ % title "Grid interpolation"+ where [x, y] = take 2 $ chunksOf 200 $ map (\x -> 4 * (x - 0.5)) $ uniforms+ z = zipWith (\x y -> x*(exp $ -(x**2) - y**2)) x y+ xi = mapLinear (\x -> x) (-2.1) 2.1 300+ yi = mapLinear (\x -> x) (-2.1) 2.1 300