The Holy Java

Building the right thing, building it right, fast

Creating A Chart With A Logarithmic Axis In Incanter 1.5.1

Posted by Jakub Holý on July 12, 2013

Incanter 1.5.1 doesn’t support logarithmic axes, fortunately it is easy to add one manually.

Update: Pushed improved version to Incanter.

This is how our final code will look like:

;; core and charts are the incanter namespaces
(defn plot-power []
  (let [fun #(Math/pow 10 %)
        y-axis (log-axis :label "log(x)")
        chart (charts/function-plot fun 0 5)]
    (set-axis chart :y y-axis)
    (core/view chart :window-title "LogAxis Test: Incanter fun plot")))

And this is the supporting code:

(defn log-axis
" Create a logarithmic axis.

  Beware: The data may not contain zero. Otherwise the chart will look
  rather strange (log doesn't like zeros).

  Options: :base (default 10) base of the logarithm; typically 2 or 10
    :label (default none) the label of the axis
"
  [& options]
  (let [opts (when options (apply assoc {} options))
        base (or (:base opts) 10)
        label (:label opts)]
    (doto (if label
            (LogAxis. label)
            (LogAxis.))
      (.setBase base)
      ;; Use normal numbers instead of 10^num, i.e. 1 inst. of 10^0.0
      (.setStandardTickUnits (NumberAxis/createIntegerTickUnits)))))

(defmulti set-axis
  "Set the selected axis of the chart, returning the chart.
  (Beware: the axis' label will replace the x-label or y-label set previously on the chart.)

  Arguments:
    chart - the JFreeChart object whose axis to change
    dimension - depends on the plot type, f.ex. :x or :y for an XYPlot
    axis - the axis to set, an instance of ValueAxis

  Examples: (use '(incanter core charts))

    (doto (function-plot #(Math/pow 10 %) 0 5) (set-axis :x (log-axis
      :base 10, :label \"log(x)\")) view)
"

  (fn [chart dimension axis] (type (.getPlot chart))))

(defmethod set-axis org.jfree.chart.plot.XYPlot
  ([chart dimension axis]
     {:pre [(#{:x :y} dimension)]}
     (let [plot (.getXYPlot chart)]
       (if (= :x dimension)
         (.setDomainAxis plot axis)
         (.setRangeAxis plot axis)))
     chart))

Admittedly, this is a little overengineered with the multimethod, but there were reasons for it :-).

About these ads

Sorry, the comment form is closed at this time.

 
%d bloggers like this: