matplotlib 0.2.1 → 0.3.0
raw patch · 5 files changed
+281/−118 lines, 5 filesdep +randomdep +raw-strings-qqdep +splitPVP ok
version bump matches the API change (PVP)
Dependencies added: random, raw-strings-qq, split
API changes (from Hackage documentation)
- Graphics.Matplotlib: gridLines :: Matplotlib
- Graphics.Matplotlib: mplotSubplot :: (MplotValue val2, MplotValue val1, MplotValue val) => val2 -> val1 -> val -> Matplotlib
- Graphics.Matplotlib: showHistogram :: (ToJSON t, MplotValue val) => t -> val -> IO (Either String String)
+ Graphics.Matplotlib: acorr :: ToJSON a => a -> Matplotlib
+ Graphics.Matplotlib: axhline :: MplotValue val => val -> Matplotlib
+ Graphics.Matplotlib: boxplot :: ToJSON a => a -> Matplotlib
+ Graphics.Matplotlib: colorbar :: Matplotlib
+ Graphics.Matplotlib: errorbar :: (ToJSON t, ToJSON t1, ToJSON t2) => t2 -> t1 -> t -> Matplotlib
+ Graphics.Matplotlib: getSubplot :: (MplotValue val2, MplotValue val1, MplotValue val) => val2 -> val1 -> val -> Matplotlib
+ Graphics.Matplotlib: grid :: Bool -> Matplotlib
+ Graphics.Matplotlib: histogram2D :: ToJSON t => t -> t -> Matplotlib
+ Graphics.Matplotlib: legend :: Matplotlib
+ Graphics.Matplotlib: pcolor :: ToJSON a => a -> Matplotlib
+ Graphics.Matplotlib: setSubplot :: MplotValue val => val -> Matplotlib
+ Graphics.Matplotlib: setTeX :: Bool -> Matplotlib
+ Graphics.Matplotlib: setUnicode :: Bool -> Matplotlib
+ Graphics.Matplotlib: subplots :: Matplotlib
+ Graphics.Matplotlib: text :: (MplotValue val2, MplotValue val1, MplotValue val) => val2 -> val1 -> val -> Matplotlib
+ Graphics.Matplotlib: xacorr :: (ToJSON t, ToJSON a) => a -> t -> [Option] -> Matplotlib
+ Graphics.Matplotlib: xcorr :: (ToJSON t, ToJSON t1) => t1 -> t -> Matplotlib
+ Graphics.Matplotlib.Internal: instance Graphics.Matplotlib.Internal.MplotValue GHC.Types.Bool
- Graphics.Matplotlib: code :: Matplotlib -> IO (Either a String)
+ Graphics.Matplotlib: code :: Matplotlib -> IO String
Files
- README.md +3/−2
- matplotlib.cabal +4/−1
- src/Graphics/Matplotlib.hs +138/−79
- src/Graphics/Matplotlib/Internal.hs +12/−2
- test/Spec.hs +124/−34
README.md view
@@ -1,6 +1,7 @@ # Matplotlib -[](https://circleci.com/gh/abarbu/matplotlib-haskell/tree/master)+[](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 fully-fledged plotting library!@@ -25,7 +26,7 @@ on Ubuntu machines with the following command: ```bash-sudo apt-get install -y python3-matplotlib python3-numpy python-mpltoolkits.basemap+sudo apt-get install -y python3-pip python3-matplotlib python3-numpy python-mpltoolkits.basemap ``` If you have instructions for other machines or OSes let me know. We require
matplotlib.cabal view
@@ -1,5 +1,5 @@ name: matplotlib-version: 0.2.1+version: 0.3.0 synopsis: Bindings to Matplotlib; a Python plotting library description: Matplotlib is probably the most full featured plotting library out there.@@ -41,6 +41,9 @@ , tasty , tasty-hunit , temporary+ , random+ , raw-strings-qq+ , split ghc-options: -threaded -rtsopts -with-rtsopts=-N default-language: Haskell2010
src/Graphics/Matplotlib.hs view
@@ -65,15 +65,117 @@ onscreen m = withMplot m (\str -> python $ pyIncludes ++ str ++ pyDetach ++ pyOnscreen) -- | Print the python code that would be executed-code :: Matplotlib -> IO (Either a String)-code m = withMplot m (\str -> return $ Right $ unlines $ pyIncludes ++ str ++ pyDetach ++ pyOnscreen)+code :: Matplotlib -> IO String+code m = withMplot m (\str -> return $ unlines $ pyIncludes ++ str ++ pyDetach ++ pyOnscreen) -- | Save to a file figure :: [Char] -> Matplotlib -> IO (Either String String) figure filename m = withMplot m (\str -> python $ pyIncludes ++ str ++ pyFigure filename) --- * Plotting commands+-- * Useful plots +-- | 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)+ % addSubplot 2 1 1+ % xcorr xs ys @@ opts+ % grid True+ % axhline 0 @@ [o1 "0", o2 "color" "'black'", o2 "lw" "2"]+ % addSubplot 2 1 2 @@ [o2 "sharex" "ax"]+ % acorr xs @@ opts+ % grid True+ % axhline 0 @@ [o2 "color" "'black'", o2 "lw" "2"]++-- | Plot a histogram for the given values with 'bins'+histogram :: (MplotValue val, ToJSON t) => t -> val -> Matplotlib+histogram values bins = readData [values] % dataHistogram 0 bins++-- | Plot a 2D histogram for the given values with 'bins'+histogram2D x y = readData [x,y] %+ mp # "plot.hist2d(data[0], data[1]" ## ")"++-- | Plot the given values as a scatter plot+scatter :: (ToJSON t1, ToJSON t) => t1 -> t -> Matplotlib+scatter x y = plot x y `def` [o1 "'.'"]++-- | Plot a line+line :: (ToJSON t1, ToJSON t) => t1 -> t -> Matplotlib+line x y = plot x y `def` [o1 "'-'"]++-- | Like 'plot' but takes an error bar value per point+errorbar xs ys errs = readData (xs, ys, errs)+ % mp # "ax.errorbar(data[0], data[1], yerr=data[2]" ## ")"++-- | Plot a line given a function that will be executed for each element of+-- given list. The list provides the x values, the function the y values.+lineF :: (ToJSON a, ToJSON b) => (a -> b) -> [a] -> Matplotlib+lineF f l = plot l (map f l) `def` [o1 "'-'"]++boxplot l = readData l+ % mp # "ax.boxplot(data" ## ")"++-- | Given a grid of x and y values and a number of steps call the given+-- function and plot the 3D contour+contourF :: (ToJSON val, MplotValue val, Ord val) => (Double -> Double -> val) -> Double -> Double -> Double -> Double -> Double -> Matplotlib+contourF f xStart xEnd yStart yEnd steps = contour xs ys zs+ where xs = mapLinear (\x -> (mapLinear (\_ -> x) yStart yEnd steps)) xStart xEnd steps+ ys = mapLinear (\_ -> (mapLinear (\y -> y) yStart yEnd steps)) xStart xEnd steps+ zs = mapLinear (\x -> (mapLinear (\y -> f x y) yStart yEnd steps)) xStart xEnd steps++-- | Given a grid of x and y values and a number of steps call the given+-- function and plot the 3D projection+projectionsF :: (ToJSON val, MplotValue val, Ord val) => (Double -> Double -> val) -> Double -> Double -> Double -> Double -> Double -> Matplotlib+projectionsF f xStart xEnd yStart yEnd steps = projections xs ys zs+ where xs = mapLinear (\x -> (mapLinear (\_ -> x) yStart yEnd steps)) xStart xEnd steps+ ys = mapLinear (\_ -> (mapLinear (\y -> y) yStart yEnd steps)) xStart xEnd steps+ zs = mapLinear (\x -> (mapLinear (\y -> f x y) yStart yEnd steps)) xStart xEnd steps++-- | Plot x against y interpolating with n steps+plotInterpolated :: (MplotValue val, ToJSON t, ToJSON t1) => t1 -> t -> val -> Matplotlib+plotInterpolated x y n =+ readData (x, y)+ % interpolate 0 1 n+ % dataPlot 0 1 `def` [o1 "-"]++-- | A handy function to plot a line between two points give a function and a number o steps+plotMapLinear :: ToJSON b => (Double -> b) -> Double -> Double -> Double -> Matplotlib+plotMapLinear f s e n = line xs ys+ where xs = mapLinear (\x -> x) s e n+ ys = mapLinear (\x -> f x) s e n++-- | Plot a line between 0 and the length of the array with the given y values+line1 :: (Foldable t, ToJSON (t a)) => t a -> Matplotlib+line1 y = line [0..length y] y++-- | Plot a matrix+matShow :: ToJSON a => a -> Matplotlib+matShow d = readData d+ % (mp # "plot.matshow(data" ## ")")++-- | Plot a matrix+pcolor :: ToJSON a => a -> Matplotlib+pcolor d = readData d+ % (mp # "plot.pcolor(np.array(data)" ## ")")++-- | Plot a KDE of the given functions; a good bandwith will be chosen automatically+density :: [Double] -> Maybe (Double, Double) -> Matplotlib+density l maybeStartEnd =+ densityBandwidth l (((4 * (variance ** 5)) / (fromIntegral $ 3 * length l)) ** (1 / 5) / 3) maybeStartEnd+ where mean = foldl' (+) 0 l / (fromIntegral $ length l)+ variance = foldl' (+) 0 (map (\x -> sqr (x - mean)) l) / (fromIntegral $ length l)+ sqr x = x * x++-- * Matplotlib configuration++setTeX :: Bool -> Matplotlib+setTeX b = mp # "matplotlib.rcParams['text.usetex'] = " # b++setUnicode :: Bool -> Matplotlib+setUnicode b = mp # "matplotlib.rcParams['text.latex.unicode'] = " # b+++-- * Basic plotting commands+ -- | Plot the 'a' and 'b' entries of the data object dataPlot :: (MplotValue val, MplotValue val1) => val1 -> val -> Matplotlib dataPlot a b = mp # "p = plot.plot(data[" # a # "], data[" # b # "]" ## ")"@@ -82,9 +184,9 @@ plot :: (ToJSON t, ToJSON t1) => t1 -> t -> Matplotlib plot x y = readData (x, y) % dataPlot 0 1 --- | Show grid lines-gridLines :: Matplotlib-gridLines = mp # "ax.grid(True)"+-- | Show/hide grid lines+grid :: Bool -> Matplotlib+grid t = mp # "plot.gca().grid(" # t # ")" -- | Plot x against y where x is a date. -- xunit is something like 'weeks', yearStart, monthStart, dayStart are an offset to x.@@ -99,49 +201,28 @@ -- | Add a label to the x axis xLabel :: MplotValue val => val -> Matplotlib-xLabel label = mp # "plot.xlabel('" # label # "')"+xLabel label = mp # "plot.xlabel(r'" # label # "'" ## ")" -- | Add a label to the y axis yLabel :: MplotValue val => val -> Matplotlib-yLabel label = mp # "plot.ylabel('" # label # "')"+yLabel label = mp # "plot.ylabel(r'" # label # "'" ## ")" -- | Add a label to the z axis zLabel :: MplotValue val => val -> Matplotlib-zLabel label = mp # "plot.zlabel('" # label # "')"+zLabel label = mp # "plot.zlabel(r'" # label # "'" ## ")" -- | Create a histogram for the 'a' entry of the data array dataHistogram :: (MplotValue val1, MplotValue val) => val1 -> val -> Matplotlib dataHistogram a bins = mp # "plot.hist(data[" # a # "]," # bins ## ")" --- | Plot a histogram for the given values with 'bins'-histogram :: (MplotValue val, ToJSON t) => t -> val -> Matplotlib-histogram values bins = readData [values] % dataHistogram 0 bins---- | Plot & show the histogram-showHistogram :: (ToJSON t, MplotValue val) => t -> val -> IO (Either String String)-showHistogram values bins = onscreen $ histogram values bins- -- | Create a scatter plot accessing the given fields of the data array dataScatter :: (MplotValue val1, MplotValue val) => val1 -> val -> Matplotlib dataScatter a b = dataPlot a b `def` [o1 "'.'"] --- | Plot the given values as a scatter plot-scatter :: (ToJSON t1, ToJSON t) => t1 -> t -> Matplotlib-scatter x y = plot x y `def` [o1 "'.'"]- -- | Create a line accessing the given entires of the data array dataLine :: (MplotValue val1, MplotValue val) => val1 -> val -> Matplotlib dataLine a b = dataPlot a b `def` [o1 "'-'"] --- | Plot a line-line :: (ToJSON t1, ToJSON t) => t1 -> t -> Matplotlib-line x y = plot x y `def` [o1 "'-'"]---- | Plot a line given a function that will be executed for each element of--- given list. The list provides the x values, the function the y values.-lineF :: (ToJSON a, ToJSON b) => (a -> b) -> [a] -> Matplotlib-lineF f l = plot l (map f l) `def` [o1 "'-'"]- -- | Create a 3D contour contour xs ys zs = readData (xs, ys, zs)@@ -157,25 +238,9 @@ % contourRaw 0 1 2 (maximum2 xs) (maximum2 ys) (minimum2 zs) % axis3DLabels xs ys zs --- | Given a grid of x and y values and a number of steps call the given--- function and plot the 3D contour-contourF :: (ToJSON val, MplotValue val, Ord val) => (Double -> Double -> val) -> Double -> Double -> Double -> Double -> Double -> Matplotlib-contourF f xStart xEnd yStart yEnd steps = contour xs ys zs- where xs = mapLinear (\x -> (mapLinear (\_ -> x) yStart yEnd steps)) xStart xEnd steps- ys = mapLinear (\_ -> (mapLinear (\y -> y) yStart yEnd steps)) xStart xEnd steps- zs = mapLinear (\x -> (mapLinear (\y -> f x y) yStart yEnd steps)) xStart xEnd steps---- | Given a grid of x and y values and a number of steps call the given--- function and plot the 3D projection-projectionsF :: (ToJSON val, MplotValue val, Ord val) => (Double -> Double -> val) -> Double -> Double -> Double -> Double -> Double -> Matplotlib-projectionsF f xStart xEnd yStart yEnd steps = projections xs ys zs- where xs = mapLinear (\x -> (mapLinear (\_ -> x) yStart yEnd steps)) xStart xEnd steps- ys = mapLinear (\_ -> (mapLinear (\y -> y) yStart yEnd steps)) xStart xEnd steps- zs = mapLinear (\x -> (mapLinear (\y -> f x y) yStart yEnd steps)) xStart xEnd steps- -- | Enable 3D projection axis3DProjection :: Matplotlib-axis3DProjection = mp # "ax = plot.figure().gca(projection='3d')"+axis3DProjection = mp # "ax = plot.gca(projection='3d')" -- | Plot a 3D wireframe accessing the given elements of the data array wireframe :: (MplotValue val2, MplotValue val1, MplotValue val) => val2 -> val1 -> val -> Matplotlib@@ -217,10 +282,10 @@ mp # "ax.bar(np.arange(len(data[" # a # "]))+" # offset # ", data[" # a # "], " # width ## ")" @@ opts -- | Create a subplot with the coordinates (r,c,f)-addSubplot r c f = mp # "ax = plot.figure().add_subplot(" # r # c # f ## ")"+addSubplot r c f = mp # "ax = plot.gcf().add_subplot(" # r # c # f ## ")" -- | Access a subplot with the coordinates (r,c,f)-mplotSubplot r c f = mp # "ax = plot.subplot(" # r # "," # c # "," # f ## ")"+getSubplot r c f = mp # "ax = plot.subplot(" # r # "," # c # "," # f ## ")" -- | The default bar with barDefaultWidth nr = 1.0 / (fromIntegral nr + 1)@@ -241,7 +306,7 @@ -- | Add a title title :: MplotValue val => val -> Matplotlib-title s = mp # "plot.title('" # s ## "')"+title s = mp # "plot.title(r'" # s # "'" ## ")" -- | Set the spacing of ticks on the x axis axisXTickSpacing :: (MplotValue val1, MplotValue val) => val1 -> val -> Matplotlib@@ -257,13 +322,6 @@ (mp # "data[" # b # "] = mlab.stineman_interp(np.linspace(data[" # a # "][0],data[" # a # "][-1]," # n # "),data[" # a # "],data[" # b # "],None)") % (mp # "data[" # a # "] = np.linspace(data[" # a # "][0],data[" # a # "][-1]," # n # ")") --- | Plot x against y interpolating with n steps-plotInterpolated :: (MplotValue val, ToJSON t, ToJSON t1) => t1 -> t -> val -> Matplotlib-plotInterpolated x y n =- readData (x, y)- % interpolate 0 1 n- % dataPlot 0 1 `def` [o1 "-"]- -- | Square up the aspect ratio of a plot. squareAxes :: Matplotlib squareAxes = mp # "plot.axes().set_aspect('equal')"@@ -296,21 +354,6 @@ ylim :: (MplotValue val1, MplotValue val) => val1 -> val -> Matplotlib ylim l u = mp # "plot.ylim([" # l # "," # u # "])" --- | A handy function to plot a line between two points give a function and a number o steps-plotMapLinear :: ToJSON b => (Double -> b) -> Double -> Double -> Double -> Matplotlib-plotMapLinear f s e n = line xs ys- where xs = mapLinear (\x -> x) s e n- ys = mapLinear (\x -> f x) s e n---- | Plot a line between 0 and the length of the array with the given y values-line1 :: (Foldable t, ToJSON (t a)) => t a -> Matplotlib-line1 y = line [0..length y] y---- | Plot a matrix-matShow :: ToJSON a => a -> Matplotlib-matShow d = readData d- % (mp # "plot.matshow(data" ## ")")- -- | Plot a KDE of the given functions with an optional start/end and a bandwidth h densityBandwidth :: [Double] -> Double -> Maybe (Double, Double) -> Matplotlib densityBandwidth l h maybeStartEnd =@@ -325,10 +368,26 @@ gaussianPdf x mu sigma = exp (- sqr (x - mu) / (2 * sigma)) / sqrt (2 * pi * sigma) sqr x = x * x --- | Plot a KDE of the given functions; a good bandwith will be chosen automatically-density :: [Double] -> Maybe (Double, Double) -> Matplotlib-density l maybeStartEnd =- densityBandwidth l (((4 * (variance ** 5)) / (fromIntegral $ 3 * length l)) ** (1 / 5) / 3) maybeStartEnd- where mean = foldl' (+) 0 l / (fromIntegral $ length l)- variance = foldl' (+) 0 (map (\x -> sqr (x - mean)) l) / (fromIntegral $ length l)- sqr x = x * x+-- | Add a horizontal line across the axis+axhline y = mp # "ax.axhline(" # y ## ")"++-- | Plot cross-correlation+xcorr x y = readData (x, y) % mp # "ax.xcorr(data[0], data[1]" ## ")"++-- | Plot auto-correlation+acorr x = readData x % mp # "ax.acorr(data" ## ")"++-- | Plot text at a specified location+text x y str = mp # "ax.text(" # x # "," # y # "," # "'" # str # "'" ## ")"++-- | Insert a legend+legend = mp # "plot.legend(" ## ")"++-- | Insert a color bar+colorbar = mp # "plot.colorbar(" ## ")"++-- | Creates subplots and stores them in an internal variable+subplots = mp # "fig, axes = plot.subplots(" ## ")"++-- | Access a subplot+setSubplot s = mp # "ax = axes[" # s # "]"
src/Graphics/Matplotlib/Internal.hs view
@@ -120,6 +120,8 @@ toPython s = show s instance MplotValue Int where toPython s = show s+instance MplotValue Bool where+ toPython s = show s instance (MplotValue x) => MplotValue (x, x) where toPython (n, v) = toPython n ++ " = " ++ toPython v instance (MplotValue (x, y)) => MplotValue [(x, y)] where@@ -134,7 +136,7 @@ -- optFn :: Matplotlib -> Matplotlib optFn :: ([Option] -> String) -> Matplotlib -> Matplotlib optFn f l | isJust $ mpPendingOption l = error "Commands can have only open option. TODO Enforce this through the type system or relax it!"- | otherwise = l' { mpPendingOption = Just (\os -> Exec (sl ++ f os)) }+ | otherwise = l' { mpPendingOption = Just (\os -> Exec (sl `combine` f os)) } where (l', (Exec sl)) = removeLast l removeLast x@(Matplotlib _ Nothing s) = (x { mpRest = sdeleteAt (S.length s - 1) s } , fromMaybe (Exec "") (slookup (S.length s - 1) s))@@ -144,6 +146,10 @@ | otherwise = Nothing sdeleteAt i s | i < S.length s = S.take i s >< S.drop (i + 1) s | otherwise = s+ combine [] r = r+ combine l [] = l+ combine l r | [last l] == "(" && [head r] == "," = l ++ tail r+ | otherwise = l ++ r -- | Merge two commands with options between options :: Matplotlib -> Matplotlib@@ -218,6 +224,8 @@ ,"import matplotlib.patches as mpatches" ,"import matplotlib.pyplot as plot" ,"import matplotlib.mlab as mlab"+ ,"import matplotlib.colors as mcolors"+ ,"import matplotlib.collections as mcollections" ,"from matplotlib import cm" ,"from mpl_toolkits.mplot3d import axes3d" ,"import numpy as np"@@ -225,7 +233,9 @@ ,"import sys" ,"import json" ,"import random, datetime"- ,"from matplotlib.dates import DateFormatter, WeekdayLocator"]+ ,"from matplotlib.dates import DateFormatter, WeekdayLocator"+ ,"ax = plot.figure().gca()"+ ,"axes = [plot.figure().gca()]"] -- | The python command that reads external data into the python data array pyReadData :: [Char] -> [[Char]]
test/Spec.hs view
@@ -1,4 +1,4 @@-{-# language ExtendedDefaultRules #-}+{-# language ExtendedDefaultRules, ScopedTypeVariables, QuasiQuotes #-} import Test.Tasty import Test.Tasty.HUnit@@ -6,37 +6,17 @@ import Graphics.Matplotlib import System.IO.Temp import System.Random--main = defaultMain tests--tests :: TestTree-tests = testGroup "Tests" [unitTests]+import Text.RawString.QQ+import Data.List+import Data.List.Split --- | Test one plot; right now we just test that the command executed without--- errors. We should visually compare plots somehow.-testPlot name fn = testCase name $ tryit fn @?= Right ""- where tryit fn = unsafePerformIO $ withSystemTempFile "a.png" (\file _ -> figure file fn)+-- * Random values for testing --- | This generates examples from the test cases-testPlot' name fn = testCase name $ tryit fn name @?= Right ""- where tryit fn name = unsafePerformIO $ figure ("/tmp/imgs/" ++ name ++ ".png") fn+uniforms :: (Random a, Num a) => [a]+uniforms = randoms (mkStdGen 42)+uniforms' lo hi = randomRs (lo,hi) (mkStdGen 42) -unitTests = testGroup "Unit tests"- [ testPlot "histogram" m1- , testPlot "cumulative" m2- , testPlot "scatter" m3- , testPlot "contour" m4- , testPlot "labelled-histogram" m5- -- TODO This test case is broken- -- , testPlot "sub-bars" $ tryit m6 "m6" @?= Right ""- , testPlot "density-bandwidth" m7- , testPlot "density" m8- , testPlot "line-function" m9- , testPlot "quadratic" m10- , testPlot "projections" m11- , testPlot "line-options" m12- , testPlot "xcorr" mxcorr- ]+-- * Not so random values to enable some fully-reproducible tests xs = [-0.54571992, 1.48409716, -0.57545561, 2.13058156, -0.75740497, -1.27879086, -0.96008858, -1.65482373, -1.69086194, -1.41925464,@@ -79,6 +59,77 @@ -0.55178235, -0.69915414, 1.35454045, 0.42931902, -1.33656935, -0.8023867 , -2.81354854, 0.39553427, -0.22235586, -1.34302011] +-- * Generate normally distributed random values; taken from normaldistribution==1.1.0.3++-- | Box-Muller method for generating two normally distributed+-- independent random values from two uniformly distributed+-- independent random values.+boxMuller :: Floating a => a -> a -> (a,a)+boxMuller u1 u2 = (r * cos t, r * sin t) where r = sqrt (-2 * log u1)+ t = 2 * pi * u2++-- | Convert a list of uniformly distributed random values into a+-- list of normally distributed random values. The Box-Muller+-- algorithms converts values two at a time, so if the input list+-- has an uneven number of element the last one will be discarded.+boxMullers :: Floating a => [a] -> [a]+boxMullers (u1:u2:us) = n1:n2:boxMullers us where (n1,n2) = boxMuller u1 u2+boxMullers _ = []++-- | Plural variant of 'normal', producing an infinite list of+-- random values instead of returning a new generator. This function+-- is analogous to 'Random.randoms'.+normals = boxMullers $ randoms (mkStdGen 42)++-- | Analogous to 'normals' but uses the supplied (mean, standard+-- deviation).+normals' (mean, sigma) g = map (\x -> x * sigma + mean) $ normals++-- * Tests++main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [unitTests]++-- | Test one plot; right now we just test that the command executed without+-- errors. We should visually compare plots somehow.+testPlot name fn = testCase name $ tryit fn @?= Right ""+ where tryit fn = unsafePerformIO $ withSystemTempFile "a.png" (\file _ -> figure file fn)++-- | This generates examples from the test cases+testPlot' name fn = testCase name $ tryit fn name @?= Right ""+ where tryit fn name = unsafePerformIO $ do+ c <- code fn+ print c+ figure ("/tmp/imgs/" ++ name ++ ".png") fn++unitTests = testGroup "Unit tests"+ [ testPlot "histogram" m1+ , testPlot "cumulative" m2+ , testPlot "scatter" m3+ , testPlot "contour" m4+ , testPlot "labelled-histogram" m5+ -- TODO This test case is broken+ -- , testPlot "sub-bars" m6+ , testPlot "density-bandwidth" m7+ , testPlot "density" m8+ , testPlot "line-function" m9+ , testPlot "quadratic" m10+ , testPlot "projections" m11+ , testPlot "line-options" m12+ , testPlot "corr" mxcorr+ , testPlot "tex" mtex+ , testPlot "show-matrix" mmat+ , testPlot "legend" mlegend+ , testPlot "hist2DLog" mhist2DLog+ , testPlot "eventplot" meventplot+ , testPlot "errorbar" merrorbar+ , testPlot "boxplot" mboxplot+ ]++-- * These tests are fully-reproducible, the output must be identical every time+ m1 :: Matplotlib m1 = histogram xs 8 @@ -116,10 +167,49 @@ m12 = plot [1,2,3,4,5,6] [1,3,2,5,2,4] @@ [o1 "'go-'", o2 "linewidth" "2"] -datas = randoms (mkStdGen 42)+-- * These tests can be random and may not be exactly the same every time -datas lo hi = randomRs (lo,hi) (mkStdGen 42)+-- | http://matplotlib.org/examples/pylab_examples/xcorr_demo.html+mxcorr = xacorr xs ys [o2 "usevlines" "True", o2 "maxlags" "50", o2 "normed" "True", o2 "lw" "2"]+ where (xs :: [Double]) = take 100 normals+ (ys :: [Double]) = take 100 normals -mxcorr = - where (xs :: Double) = take 100 datas- (ys :: Double) = take 100 datas+-- | http://matplotlib.org/examples/pylab_examples/tex_unicode_demo.html+mtex = plotMapLinear cos 0 1 100+ % setTeX True+ % setUnicode True+ % 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++mmat = pcolor (take 10 $ chunksOf 8 uniforms) @@ [o2 "edgecolors" "'k'", o2 "linewidth" "1"]++-- | http://matplotlib.org/examples/pylab_examples/legend_demo3.html+mlegend = plotMapLinear (\x -> x ** 2) 0 1 100 @@ [o2 "label" "'x^2'"]+ % plotMapLinear (\x -> x ** 3) 0 1 100 @@ [o2 "label" "'x^3'"]+ % legend @@ [o2 "fancybox" "True", o2 "shadow" "True", o2 "title" "'Legend'", o2 "loc" "'upper left'"]++-- | http://matplotlib.org/examples/pylab_examples/hist2d_log_demo.html+mhist2DLog = histogram2D x y @@ [o2 "bins" "40", o2 "norm" "mcolors.LogNorm()"]+ % colorbar+ where (x:y:_) = chunksOf 10000 normals++meventplot = plot xs ys+ % mp # "ax.add_collection(mcollections.EventCollection(data[0], linelength=0.05))"+ % mp # "ax.add_collection(mcollections.EventCollection(data[1], orientation='vertical', linelength=0.05))"+ % text 0.1 0.6 "Ticks mark the actual data points"+ where xs = sort $ take 10 uniforms+ ys = map (\x -> x ** 2) xs++merrorbar = errorbar xs ys errs @@ [o2 "errorevery" "2"]+ where xs = [0.1,0.2..4]+ ys = map (\x -> exp $ -x) xs+ errs = map (\x -> 0.1 + 0.1 * sqrt x) xs++mboxplot = subplots @@ [o2 "ncols" "2", o2 "sharey" "True"]+ % setSubplot "0"+ % boxplot (take 3 $ chunksOf 10 $ map (* 2) $ normals) @@ [o2 "labels" "['X', 'Y', 'Z']"]+ % setSubplot "1"+ % boxplot (take 3 $ chunksOf 10 $ map (* 2) $ normals) @@ [o2 "labels" "['A', 'B', 'C']", o2 "showbox" "False", o2 "showcaps" "False"]+