← На главную

Строим диаграммы с помощью Scala Chart

Сегодня мы научимся строить графики, гистограммы и круговые диаграммы в Scala. В этом нам поможет замечательная библиотека Scala Chart, которая представляет собой обертку над широко известной в мире Java библиотекой JFreeChart. Также будут объяснены некоторые интересные возможности и особенности Scala, которые ранее не рассматривались в этом блоге.

Нам понадобится примерно такой build.sbt:

name := "scala-chart-examples" version := "0.1" scalaVersion := "2.10.1" libraryDependencies += "com.github.wookietreiber" %% "scala-chart" % "0.2.0" scalacOptions ++= Seq("-unchecked", "-deprecation")

Если вы хотите использовать самую последнюю версию Scala Chart, то вместо конкретной версии "0.2.0" можете написать "latest.integration". Но если хотите знать мое мнение, это неразумно.

Рассмотрим построение простейшего графика:

package me.eax.scalachart import scalax.chart._ import scalax.chart.Charting._ import java.io._ object Application extends App { val defaultChartSize = (512, 384) def createSimpleXYLineChart { val dataset = Seq((1,2),(2,4),(3,6),(4,8)) val chart = XYLineChart( dataset.toXYSeriesCollection("f(x)"), title = "XYLineChart Simple Example", domainAxisLabel = "x axis description", rangeAxisLabel = "y axis description" ) chart.saveAsPNG(new File("./simpleXYLineChart.png"), defaultChartSize) }

С кортежами мы уже знакомы. Seq – это трейт (трейты в первом приближении можно считать аналогами интерфейсов в Java или абстрактных классов в C++) для последовательностей. С последовательностями мы тоже уже сталкивались, но не предавали этому особого значения. Последовательности похожи на итерируемые контейнеры, только элементы в последовательностях всегда упорядочены.

Вызов Seq(1,2,3) представляет собой всего лишь вызов метода apply класса-одиночки scala.collection.Seq, который возвращает обыкновенный односвязный список из переданных элементов:

scala> Seq(1,2,3) res0: Seq[Int] = List(1, 2, 3)

Обратите внимание на создание экземпляра класса XYLineChart. Тут были использованы именованные параметры, с чем мы раньше не сталкивались. Эта возможность очень удобна в случае, если у метода много аргументов, и некоторые из них имеют значения по умолчанию. Также возникает вопрос, откуда у класса List взялся метод toXYSeriesCollection? В действительности, тут используется неявное приведение типов. Если в двух словах, вот как это работает:

scala> class MyIterable[T](it: Iterable[T]) { def secret { ⏎ it foreach println } } defined class MyIterable scala> implicit def iterableToMyIterable[T](x: Iterable[T]) = new MyIterable(x) iterableToMyIterable: [T](x: Iterable[T])MyIterable[T] scala> List(1,2,3).secret 1 2 3

Класс List не имеет метода secret, однако, с помощью iterableToMyIterable, он может быть неявным образом преобразован в MyIterable, у которого этот метод есть. Компилятор Scala автоматически преобразует последнюю строку кода в iterableToMyIterable(List(1,2,3)).secret, в результате чего у класса List «появляется новый метод». Кстати, в Scala 2.10, благодаря появившийся поддержке implicit-классов, можно обойтись без вспомогательной функции.

Далее с помощью метода saveAsPNG мы сохраняем график в PNG-файл:

Простой график, построенный в Scala Chart

Существуют аналогичные методы для форматов JPEG и PDF. Кроме того, с помощью метода show можно вывести график в окне, созданном с помощью Swing.

Рассмотрим чуть более сложный пример:

def createXYLineChart { val dataset = Seq( ("x*x - 2*x", for(x <- -3.0 to 8.0 by 1.0) yield (x, x*x - 2 * x)), ("3 * x", for(x <- 1.0 to 10.0 by 0.1) yield (x, 3 * x)) ) val chart = XYLineChart( dataset.toXYSeriesCollection, title = "XYLineChart Example", domainAxisLabel = "время, с", rangeAxisLabel = "скорость, м/c" ) chart.saveAsPNG(new File("./xyLineChart.png"), defaultChartSize) }

Для нас здесь нет ничего нового. Полученные графики:

Графики, построенные в Scala Chart

Иногда требуется построить график не по координатам точек, а, например, по месяцам и количеству переходов на сайт в эти месяцы. Для решения этой задачи в Scala Chart предусмотрен класс LineChart:

val categoryDatasetExample = Map( "январь" -> Map("заработано" -> 2, "потрачено" -> 4), "февраль" -> Map("заработано" -> 4, "потрачено" -> 5), "март" -> Map("заработано" -> 8, "потрачено" -> 6), "апрель" -> Map("заработано" -> 16, "потрачено" -> 7) ).toCategoryDataset def createLineChart { val chart = LineChart( categoryDatasetExample, title = "LineChart Example", domainAxisLabel = "месяц", rangeAxisLabel = "млн рублей" ) chart.saveAsPNG(new File("./lineChart.png"), defaultChartSize) }

Здесь мы используем контейнер immutable.Map. Можно было использовать его и в предыдущем примере, в этом смысле библиотека Scala Chart является очень гибкой. Что интересно, -> – это не часть синтаксиса Scala, а обычная функция, возвращающая пару из переданных ей аргументов.

Полученные графики:

Использование класса LineChart

Построение круговой диаграммы:

def createPieChart { val dataset = Map( "Erlang" -> 33.3, "Scala" -> 33.3, "Clojure" -> 13.4, "Haskell" -> 10.0, "OCaml" -> 10.0 ) val chart = PieChart.threeDimensional( dataset.toPieDataset, legend = false, title = """|Результаты опроса |"Ваш любимый язык программирования" |""".stripMargin ) chart.saveAsPNG(new File("./pieChart.png"), defaultChartSize) }

Диаграмма:

Круговая диаграмма, построенная в Scala Chart

Построение простой гистограммы:

def createSimpleBarChart { val dataset = Map("alpha" -> 123.45, "beta" -> 678.9) val chart = BarChart( dataset.toCategoryDataset, title = "Simple Bar Chart Example" ) chart.saveAsPNG(new File("./simpleBarChart.png"), defaultChartSize) }

Результат:

Простая гистограмма, построенная в Scala Chart

Чуть более сложная гистограмма:

def createBarChart { val chart = BarChart( categoryDatasetExample, // см выше title = "Bar Chart Example" ) chart.saveAsPNG(new File("./barChart.png"), defaultChartSize) }

Результат:

Более сложная гистограмма, созданная с помощью Scala Chart

Также Scala Chart умеет строить некоторые другие виды диаграмм. Подробности вы найдете в официальной ScalaDoc. Полная версия приведенного выше кода доступна в этом архиве.

Дополнение: Вас также могут заинтересовать статьи Работа с Excel-файлами в Scala и Построение диаграмм на Python с помощью Matplotlib.