Сегодня мы научимся строить графики, гистограммы и круговые диаграммы в Scala. В этом нам поможет замечательная библиотека Scala Chart , которая представляет собой обертку над широко известной в мире Java библиотекой JFreeChart . Также будут объяснены некоторые интересные возможности и особенности Scala, которые ранее не рассматривались в этом блоге.
Нам понадобится примерно такой build.sbt :
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"
. Но если хотите знать мое мнение, это неразумно.
Рассмотрим построение простейшего графика:
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, который возвращает обыкновенный односвязный список из переданных элементов:
res0: Seq[Int] = List(1, 2, 3)
Обратите внимание на создание экземпляра класса XYLineChart. Тут были использованы именованные параметры, с чем мы раньше не сталкивались. Эта возможность очень удобна в случае, если у метода много аргументов, и некоторые из них имеют значения по умолчанию. Также возникает вопрос, откуда у класса List взялся метод toXYSeriesCollection? В действительности, тут используется неявное приведение типов. Если в двух словах, вот как это работает:
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-файл:
Существуют аналогичные методы для форматов JPEG и PDF. Кроме того, с помощью метода show можно вывести график в окне, созданном с помощью Swing .
Рассмотрим чуть более сложный пример:
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 предусмотрен класс LineChart:
«январь» — > 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, а обычная функция, возвращающая пару из переданных ей аргументов.
Полученные графики:
Построение круговой диаграммы:
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 )
}
Диаграмма:
Построение простой гистограммы:
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 )
}
Результат:
Чуть более сложная гистограмма:
val chart = BarChart (
categoryDatasetExample, // см выше
title = «Bar Chart Example»
)
chart. saveAsPNG ( new File ( «./barChart.png» ) , defaultChartSize )
}
Результат:
Также Scala Chart умеет строить некоторые другие виды диаграмм. Подробности вы найдете в официальной ScalaDoc . Полная версия приведенного выше кода доступна в этом архиве .
Дополнение: Вас также могут заинтересовать статьи Работа с Excel-файлами в Scala и Построение диаграмм на Python с помощью Matplotlib .