Доброго времени суток, %username%!
Графики — наглядный способ представления информации. Картинка стоит тысячи слов, а график в некоторых случаях часто полностью описывает результаты эксперимента, физического или вычислительного. В конце концов, мне нравятся графики.
Однажды я оказался в ситуации, что данные для построения графика уже есть, а компьютера под рукой нет. Но ведь с такой задачей может справиться и смартфон! Так я смог заставить себя начать применять знания, полученные из наполовину пройденного курса по Python от Google, и использовать sl4a, уже успевший покрыться виртуальной пылью. Итак, для работы понадобятся sl4a (кто еще не слышал такую аббревиатуру, прочитайте это, это и это) + flot (подойдет любая библиотека для построения графиков на js).
Вдоль оси абсцисс будем отсчитывать номер наблюдаемой величины, вдоль оси ординат — её значение. Значения вычисляются следующим образом: есть 30 логов, содержащих строчки вида «value = 0.1 0.2 0.15 0.12 ...», где «0.1 0.2 ...» — значения, «value» — название величины. Значение «1» есть среднее по всем первым числам из соответствующих строк логов («0.1» в примере строки), «2» — по всем вторым и т.д. В итоге получается двумерная матрица размера M*N, где M — количество строк в логе, N — количество чисел в строке. Предполагается, что логи содержат одинаковое количество строк и одинаковое количество чисел в каждой строке.
За построение графика отвечает плагин flot к jquery. Из полного комплекта с сайта разработчиков для нашей задачи понадобятся только файлы jquery.flot.js и jquery.js. Сам код log_manager.html:
Строка 13 — построение графика с помощью flot. Например, можно написать
и на графике отобразится кусок параболы. Таким образом, данные для построения должны иметь вид [[x0,y0],[x1,y1],[x2,y2],[x3,y3], ...]. Самый простой способ, пришедший мне в голову — подготовить их в python-скрипте в строку точно такого же вида и обернуть в javascript в eval(), которая выполнит переданную строку как если бы это был кусок js-кода. Далее я использую именно этот способ.
Модификация существующих и добавление новых свойств отображения кривых на графике реализуется просто. Например, чтобы отключить тень под кривой, достаточно добавить «shadowSize: 0»:
Две кривых на одном графике:
В строке 14 создается объект для взаимодействия с Android API (его возвращает встроеная в sl4a функция «Android()»).
В 15 строке описывается, как обрабатывать полученный event с именем «plotData». Как только получен event с таким именем, вызывается функция «plotData». Переданные с ним данные (строка-массив) будут находиться в <имя_входной_переменной_в_функции>.data.
Осталось только написать скрипт, который файлы прочитает, строку подготовит и пошлет её. Об этом следующая часть.
Код log_manager.py:
Данный код плох, так писать не стоит. Но свою задачу он выполняет: получает матрицу, состоящую из соответствующих средних по логам.
В строках 1-43 происходит считывание данных из файлов в двумерный массив value. В 44-48 подготавливается строка для вывода графика в flot. В 50-60 создается webView на основе странички log_manager.html (54), ждем 3 секунды, скрипт ждет, пока страничка загрузится (плохой подход!) (57), и посылает событие с данными для построения графика (60).
Чтобы протестировать написанный скрипт, необходимо положить log_manager.py, log_manager.html, jquery.flot.js и jquery.js в папку /sdcard/sl4a/scripts. В корне карты памяти должны лежать файлы с именами «864x864x30-0-of-30.log»...«864x864x30-29-of-30.log». В каждом логе должно быть записано записано одинаковое число строк вида «value = 0.2 0.34 0.343 ...» с одинаковым количеством чисел в каждой строке. Скрипт построит график на основе средних значений последних строк логов и имеющий вид:
Архив с файлами, упомянутыми в статье. Также в архиве присутствуют jquery.js и jquery.flot.js из комплекта с сайта flot.
Введение
Графики — наглядный способ представления информации. Картинка стоит тысячи слов, а график в некоторых случаях часто полностью описывает результаты эксперимента, физического или вычислительного. В конце концов, мне нравятся графики.
Однажды я оказался в ситуации, что данные для построения графика уже есть, а компьютера под рукой нет. Но ведь с такой задачей может справиться и смартфон! Так я смог заставить себя начать применять знания, полученные из наполовину пройденного курса по Python от Google, и использовать sl4a, уже успевший покрыться виртуальной пылью. Итак, для работы понадобятся sl4a (кто еще не слышал такую аббревиатуру, прочитайте это, это и это) + flot (подойдет любая библиотека для построения графиков на js).
Вдоль оси абсцисс будем отсчитывать номер наблюдаемой величины, вдоль оси ординат — её значение. Значения вычисляются следующим образом: есть 30 логов, содержащих строчки вида «value = 0.1 0.2 0.15 0.12 ...», где «0.1 0.2 ...» — значения, «value» — название величины. Значение «1» есть среднее по всем первым числам из соответствующих строк логов («0.1» в примере строки), «2» — по всем вторым и т.д. В итоге получается двумерная матрица размера M*N, где M — количество строк в логе, N — количество чисел в строке. Предполагается, что логи содержат одинаковое количество строк и одинаковое количество чисел в каждой строке.
Реализация
Отображение графика с помощью flot
За построение графика отвечает плагин flot к jquery. Из полного комплекта с сайта разработчиков для нашей задачи понадобятся только файлы jquery.flot.js и jquery.js. Сам код log_manager.html:
<html>
<head>
<title>Plot</title>
<link href="layout.css" rel="stylesheet" type="text/css">
<script src="jquery.js"></script>
<script src="jquery.flot.js"></script>
</head>
<body>
<div id="placeholder" style="width:535px;height:270px;"></div>
<script>
var plotData = function(d) {$.plot($("#placeholder"), [ {label: "flux", data: eval(d.data), color: "rgb(255, 100, 100)" }] );};
var droid = new Android();
droid.registerCallback("plotData", plotData);
</script>
</body>
</html>
Строка 13 — построение графика с помощью flot. Например, можно написать
$.plot($("#placeholder"), [ {label: "flux", data: [[0,0],[1,1],[2,4],[3,9],[4,16]], color: "rgb(255, 100, 100)" }] );
и на графике отобразится кусок параболы. Таким образом, данные для построения должны иметь вид [[x0,y0],[x1,y1],[x2,y2],[x3,y3], ...]. Самый простой способ, пришедший мне в голову — подготовить их в python-скрипте в строку точно такого же вида и обернуть в javascript в eval(), которая выполнит переданную строку как если бы это был кусок js-кода. Далее я использую именно этот способ.
Модификация существующих и добавление новых свойств отображения кривых на графике реализуется просто. Например, чтобы отключить тень под кривой, достаточно добавить «shadowSize: 0»:
$.plot($("#placeholder"), [ {label: "flux", data: [[0,0],[1,1],[2,4],[3,9],[4,16]], shadowSize: 0, color: "rgb(255, 100, 100)" }] );
Две кривых на одном графике:
$.plot($("#placeholder"), [ {label: "flux", data: [[0,0],[1,1],[2,4],[3,9],[4,16],[5,25]], shadowSize: 0, color: "rgb(255, 100, 100)" }, {label: "flux", data: [[0,0],[5,25]], shadowSize: 0, color: "rgb(255, 100, 100)" }] );
В строке 14 создается объект для взаимодействия с Android API (его возвращает встроеная в sl4a функция «Android()»).
В 15 строке описывается, как обрабатывать полученный event с именем «plotData». Как только получен event с таким именем, вызывается функция «plotData». Переданные с ним данные (строка-массив) будут находиться в <имя_входной_переменной_в_функции>.data.
Осталось только написать скрипт, который файлы прочитает, строку подготовит и пошлет её. Об этом следующая часть.
Подготовка данных с помощью Python
Код log_manager.py:
#!/usr/python
## Import libraries
# android for access to Android API
# time for sleep(sec)
import android, time
# Filename is "<FileCounter>-of-<NumberOfFiles>.log"
filename = "/sdcard/864x864x30-0-of-30.log"
# Get number of files
N = int(filename.split("/")[-1].split("-")[-1].split(".")[0])
# Read first file
file = open(filename,"r")
value = []
for line in file.readlines():
if "value =" in line:
value.append([])
for val in line.split(" "):
try:
value[-1].append(float(val))
except:
continue
# Read other files
for f in range(1, N):
file = open(filename.replace("-0-","-"+str(f)+"-"))
i = 0
for line in file.readlines():
if "value =" in line:
j = 0
for val in line.split(" "):
try:
value[i][j] += float(val)
j += 1
except:
continue
i += 1
# Prepare string for flot
toBePlotted = "["
for i in range(0, len(value[-1])):
toBePlotted += "[" + str(i) + "," + str(value[-1][i]) + "],"
toBePlotted += "]"
# Get droid object to use Android API
droid = android.Android()
# Set web view
droid.webViewShow('file:///sdcard/sl4a/scripts/log_manager.html')
# Wait 3 seconds while web view starts
time.sleep(3)
# Post event 'plotData' to web view
droid.eventPost('plotData', toBePlotted)
Данный код плох, так писать не стоит. Но свою задачу он выполняет: получает матрицу, состоящую из соответствующих средних по логам.
В строках 1-43 происходит считывание данных из файлов в двумерный массив value. В 44-48 подготавливается строка для вывода графика в flot. В 50-60 создается webView на основе странички log_manager.html (54), ждем 3 секунды, скрипт ждет, пока страничка загрузится (плохой подход!) (57), и посылает событие с данными для построения графика (60).
Результаты
Чтобы протестировать написанный скрипт, необходимо положить log_manager.py, log_manager.html, jquery.flot.js и jquery.js в папку /sdcard/sl4a/scripts. В корне карты памяти должны лежать файлы с именами «864x864x30-0-of-30.log»...«864x864x30-29-of-30.log». В каждом логе должно быть записано записано одинаковое число строк вида «value = 0.2 0.34 0.343 ...» с одинаковым количеством чисел в каждой строке. Скрипт построит график на основе средних значений последних строк логов и имеющий вид:
Архив с файлами, упомянутыми в статье. Также в архиве присутствуют jquery.js и jquery.flot.js из комплекта с сайта flot.