Сегодня мы научимся строить графики, гистограммы и круговые диаграммы в 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 .

EnglishRussianUkrainian