COVID-2019 Russia pushed to GitHub
Mar. 20th, 2020 07:36 pmhttps://github.com/alexei-kouprianov/COVID.2019.ru
Вообще, такая задача встречается крайне редко, потому что гистограммы печатаются отлично и без того, при помощи специальной функции hist()
. Более того, если зачем-то нужно именно напечатать ее при помощи plot()
, то можно записать гистограмму в объект, а потом напечатать объект при помощи plot()
:
hist.x <- hist(x) plot(hist.x)
Однако мне пришлось решить не совсем тривиальную задачу. Надо было напечатать в черно-белом варианте две просвечивающие сквозь друг друга гистограммы, назовем их A и B, с доволно узкими бинами. Будь бины широкими, разнонаправленная штриховка «читалась» бы легко, и то в глазах бы рябило, но с узкими все выглядело совсем плохо, хотя, конечно, при большом желании, читалось.
Мне же надо было сделать так, чтобы даже те, кто не имеет большого желания, поняли, что тут надо увидеть. Тогда я подумал, что было бы неплохо сделать одну из гистограмм (B) черной, оставив другую (A) заштрихованной, однако оставалась одна проблема: с определенного момента столбики B оказывались систематически выше, чем столбики A, в результате чего столбики A были бы совершенно не видны.
Тогда я подумал, что можно напечатать B поверх A, а после этого напечатать поверх них еще одну гистограмму, C, со штриховкой белого цвета, тогда будет казаться, что A просвечивает сквозь B тонкими белыми линиями.
Оставалось решить только одну проблему: A и B генерировались стандартной функцией hist()
, а откуда брать C, было ясно только в общих чертах. Теретически, надо было собрать объект «гистограмма» при помощи веревочной петли и палки, а потом подсунуть его функции plot()
для печати. По сути, гистограмма -- это определенной формы объект класса list
, в котором в виде числовых векторов хранятся данные о границах (breaks
) и центрах (mids
) бинов, их высотах (counts
) и кое-какая еще информация, уже не так нужная для того конкретного рисунка, который я собирался напечатать, так что задача не представлялась неразрешимой. Постепенно, после нескольких проб и ошибок, это смутное понимание оформилось в скрипт (имена объектов изменены):
# Недолго думая я использовал первую попытку печати гистограмм, # чтобы создать объекты hist.A и hist.B: hist.A <- hist(A$x, xlim=c(0,50), breaks=-.5:100.5, angle=45, density=22) hist.B <- hist(B$x, breaks=-.5:152.5, angle=-45, density=22, add=TRUE) counts <- NULL # Мне надо было нарисовать увеличенно только часть гистограммы, # поэтому я взял по 50 бинов от каждой (понятно, что можно было # взять обе целиком, ту, в которой бинов было меньше, дополнить # нулями в counts). for(i in 1:50){ if(hist.A$counts[i] < hist.B$counts[i]){ counts <- c(counts, hist.A$counts[i])} else{ counts <- c(counts, hist.B$counts[i])} } } breaks <- -.5:50.5 mids <- 0:50 equidist <- TRUE # Теперь собираем гистограмму: hist.C <- list( breaks, counts, mids, equidist ) # Добавляем имена элементов объекта: names(hist.C) <- c( "breaks", "counts", "mids", "equidist" ) # Добавляем специфическое имя класса ("histogram"): attributes(hist.C$class) <- "histogram"
Оставалось только напечатать все это.
hist(A$x, xlim=c(0,30), breaks=-.5:100.5, angle=45, density=22, main="", xlab="Legend for x-axis") hist(B$x, breaks=-.5:152.5, col=1, add=TRUE) plot(hist.C, col="white", density=22, add=TRUE)
Там еще легенду пришлось вручную напечатать, но это такие танцы были с rect()
и text()
, что лучше о них не рассказывать.
There are several characters that should be avoided within data fields in .csv
and .tdv
like files handled by R's read.table()
with as many default settings untouched as it is only possible.
# " '
The former gives a false impression of a comment, the two latter may be mistaken for data field borders.
Substitute:
" with ” ' with ’
Долгие годы не решался сделать это (процедура пугала заковыристостью, альтернатива в виде растрового изображения нужного размера казалась вполне достойной, но тут вопрос принципа -- можно или нет), и вот -- наконец. Начертание основного кириллического шрифта ("CM Sans Cyrillic") страшно, как смерть (надо будет поглядеть, не появилось ли удачных замен), но в генерируемых R PDF, все же, можно использовать кириллицу.
install.packages('extrafont') # Needed only once library(extrafont) # Needed every session font_install('fontcm') # Needed only once (installs fontcm fonts) font_import() # Needed after any new fonts had been installed with font_install() loadfonts() # Needed after font_import() fonts() # Preview font families fonttable() # Preview all fonts pdf("plot_cm.pdf", family="CM Sans Cyrillic", encoding="KOI8-R", width=5, height=5) plot(c(1,5), c(1,5), main="Сделано с CM fonts") text(x=3, y=3, cex=1.5, expression(italic(sum(frac(1, n*'!'), n==0, infinity) == lim(bgroup('(', 1 + frac(1, n), ')')^n, n %->% infinity)))) dev.off() embed_fonts("plot_cm.pdf", outfile="plot_cm_embed.pdf") # embed fonts, create a new and better file
Т. е., коротко говоря, после того, как все в первый раз поставили, в каждой новой сессии это будет выглядеть так:
library(extrafont) # Needed every session pdf("plot_cm.pdf", family="CM Sans Cyrillic", encoding="KOI8-R", width=5, height=5) plot(c(1,5), c(1,5), main="Сделано с CM fonts") text(x=3, y=3, cex=1.5, expression(italic(sum(frac(1, n*'!'), n==0, infinity) == lim(bgroup('(', 1 + frac(1, n), ')')^n, n %->% infinity)))) dev.off() embed_fonts("plot_cm.pdf", outfile="plot_cm_embed.pdf") # embed fonts, create a new and better fileВажно! Без
embed_fonts()
работать не будет. Обратите внимание, что embed_fonts()
создает новый файл, в котором и находится итоговая иллюстрация с интегрированными шрифтами.При написании этой памятки использовал следующие материалы:
extrafont
fontcm
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/' sudo apt update sudo apt install r-base
Год | N | X2 | p-value |
2000 | 91306 | 17.962 | p = 0.03562 |
2004 | 95777 | 47.292 | p = 3.463 x 10-7 |
2008 | 96612 | 96.036 | p < 2.2 x 10-16 |
2012 | 95414 | 42.484 | p = 2.68 x 10-6 |
2018 | 97694 | 35.029 | p = 5.89 x 10-5 |
В последние годы в кругах, причастных цифровой гуманиоре, популярны работы Льва Мановича [см. http://manovich.net/ ], придумавшего и пропагандирующего интересные формы работы с большими массивами изображений. Обычно, как я понял, он получает эти массивы по API из Instagram или аналогичных источников, потом создает базы изображений и сопутствующих метаданных и строит умопомрачительной красоты визуализации.
Часто говорят о том, что он работает с изображениями в «нередуцированном» виде. Из чтения его работ я не понял, что имеется в виду под нередуцированным видом, потому что вся умопомрачительная графика строится на том, что из графического файла извлекаются весьма немногие суммарные характеристики (обычно усредненная яркость, контрастность и время создания), после чего вся коллекция располагается в осях этих переменных. Отдельные изображения, в зависимости от размеров графика и количества изображений, могут репрезентировать точки, покрашенные в усредненный по изображению цвет, или уменьшенные / неуменьшенные копии изображений, играющие роль все тех же точек на графике, но краше.
Сам Манович предлагает использовать для работы специально созданный им программный продукт, который состоит из надстройки над Jimage. Немного подумав, я решил, что нет ничего запредельно сложного, в том, чтобы добиться того же иными средствами, не отходя далеко от R (в работе с R я вижу два плюса -- возможность создания скриптов, позволяющих воспроизводить преобразования данных, расчеты и графики, и бесконечный аналитический потенциал). При такой простоте подхода к презентации данных, ключевой вопрос был, как понятно, в том, чтобы как-то получить эти самые данные об изображениях (потому что, получив их, нарисовать типичный график несложно). Я не рассматриваю здесь вопрос о сборе метаданных (авторы, комментарии, геопозиция, количество лайков и т. п.) и получении изображений как таковых. Они лежат в несколько иной плоскости (роботы-фетчеры и API), меня интересуют вопросы об анализу уже наличных коллекций.
Если изображения уже имеются в вашем распоряжении, добыть данные об их параметрах гораздо проще, чем может показаться на первый взгляд. Помимо GUI редакторов для графики, как известно, есть довольно мощные инструменты для работы с графикой из командной строки. Я использую в таких случаях ImageMagick (да нагуглится имя его). В его состав, среди многого прочего, входит утилита identify
, которая возвращает от просмотренных ею файлов довольно изрядный список параметров. Если не указано иное, то возвращает она его на стандартное устройство вывода (экран), но можно указать иное и направить весь этот поток текста в файл. Вот несколько сокращенный real-life пример данных по одному из моих файлов (фото, сделанное цифровым фотоаппаратом).
Как нетрудно догадаться, можно заставить собрать эту информацию от всех файлов, находящихся в определенном месте. Для меня это были все подпапки в одной из папок фотоархива. Для этого пришлось написать такой простенький скрипт:
Файл imagedata.sh:
#!/bin/bash shopt -s globstar for img in **/*.JPG; do identify "$img" -verbose >> imagedata.raw.txt done
Скрипт жужжал больше часу, собрал 60+ МБ всей этой словесно-цифирной шелухи из 7+ тыс. файлов (скромно, в общем, но я мало снимаю в последние годы). Пришла пора достать оттуда то, что нужно (умные люди могут делать это на лету, думаю, и я, попыхтев немного с sed, смог бы, но пыхтеть не хотелось и я пошел по пути наименьшего сопротивления, написав [опять-таки, по мнению знатоков, наверняка избыточный] скрипт на perl). Доставать я собирался время создания, параметры каналов RGB (mean и standard deviation) и суммарные характеристики изображения, которые могут сойти за меры общей яркости и общей контрастности Overall / mean и Overall / standard deviation.
( скрипт тоже под cut... )После чего у меня образовался небольшой файлик на 1.2 МБ, который уже можно было загружать в R. После загрузки удалось нарисовать несколько картинок примерно настолько же осмысленных, как и картинки автора идеи. Самая большая возня была, как это ни смешно, с осью времени (я редко работаю со временем в таком формате), но все как-то утряслось. Их можно допиливать и дальше (например, теоретически возможен вариант с использованием сильно уменьшенных изображений в качестве точек на графиках), но это уже не принципиально.
Вот -- коллекция, развернутая в осях: яркость / контрастность:
Вот, в порядке нарастания детализации, временные ленты для яркости...:
...и контрастности: