sobota, 25 kwietnia 2020

Master-plan Jacka Sasina

Przyznam się że typa nie lubię. Oceniam go jako nietypowego partyjnego aparatczyka: mierny ale wierny, tyle że nie bierny (w tym sensie nietypowy, co w połączeniu z cechą pierwszą powoduje, że nawet gorszy niż ten co niewiele robi). Sasin działa. Jest gwiazdą TV (nie oglądam wywiadów z tą gwiazdą). Jest także autorem dwóch projektów, które zapamiętałem: 1) zmiana granic okręgów wyborczych w wyborach samorządowych celem ułatwienia wyboru kandydata PiS (Ustawa o ustroju miasta stołecznego Warszawy; więcej google: zmiana+granic+okręgów+Sasin) oraz 2) pozbawienie prezydent Łodzi Hanny Zdanowskiej biernego prawa wyborczego (Sasin o Zdanowskiej: Nawet jeśli wygra, nie będzie rządzić; więcej: google: zdanowska+sasin)

Wszystkie te projekty mają jeden wspólny mianownik: 1) dotyczyły wyborów, 2) były na wariata, 3) nie wyszły. OK, wybory 10-05-2020 mogą wyjść, bo się jeszcze nie odbyły. Jest teoretycznie szansa, że panu premierowi odwróci się karta, ale ja powiem już dziś: myślę, że wątpię. Powiem więcej: ten project jest tak na wariata, że cały czas mam przeświadczenie, że to dywersja (zwana także zasłoną dymną). Tj. prawdziwy projekt jeszcze nie został ujawniony.

Przy czym projekt wybory korespondencyjne sam w sobie nie jest zły. Nie jest ani niebezpieczny dla zdrowia (bzdura wymyślona przez opozycję) ani nie jest niekonstytucyjny (kolejna bzdura). W stanie Oregon od 1998 roku wybory są wyłącznie korespondencyjne, tyle że w Oregonie: pakiety są wysyłane trzy tygodnie przed dniem głosowania, a w zasadzie liczenia głosów (co daje możliwość reklamowania się jak się koperty nie dostało), podpisane koperty zwrotne są weryfikowane z podpisem wyborcy z rejestru (coś jak weryfikacja podpisu w banku, co mocno IMO ogranicza możliwość wysyłania głosów za kogoś), komisja potwierdza przyjęcie ważnego głosu (SMSem). Wszystkie wymienione pozwalają moim zdaniem ograniczyć możliwe fałszerstwa i omyłki. Powiem że a la Oregon to ja bym nawet wolał niż łazić do lokalu i głosować osobiście (i pewnie by taniej wyszło). No ale to co wymyślił Sasin to się ma jak pięść do nosa do procedury oregońskiej: jaka jest gwarancja formalna że każdy dostanie kartę do głosowania, jak list ma być dostarczony 3 dni przed i nierejestrowany? Jak wykryć fałszerstwa? Wolne żarty, że się będzie zatrudniać grafologa. Załóżmy, że wpłynie 50 tys skarg na fałszywy podpis pod kartą złożonych nawet złośliwie przez opozycyjnych członków komisji (mają prawo nawet do obstrukcji, podobnie jak PiS co nie miał żadnych podstaw ale składał protesty w ostatnich wyborach samorządowych, bo ,,różnica była mała''). No więc kiedy wtedy SN by uznał ważność wyborów? Za 10 lat? Albo hurtem oddalił wszystkie protesty?

Tak to widzę...

Worldometer vs ECDC

Jak już pisałem danych nt COVID19 jest multum bo są traktowane jako treść promocyjna, przyciągająca klikających. Każda tuba medialna (gazeta/portal/telewizja) w szczególności publikuje dane nt.

Źródłem pierwotnym każdego wydają się być raporty narodowe (bo jak inaczej), ale ponieważ te raporty narodowe są składane w różny sposób, to ich połączenie w jedną bazę też różnie może wyglądać. Generalnie ci co takie bazy robią to albo przyznają się, że działają na hmmm niekonwencjonalnych źródłach (Twitter) albo nic nie piszą, skąd mają dane. Mają i już...

Wydaje się (chyba, że czegoś nie wiem), że ECDC, OWiD, CSSE oraz Worldometers (dalej WMs) są najpopularniejszymi źródłami danych nt COVID19 w przekroju międzynarodowym. (Nawiasem mówiąc: WHO nie publikuje danych -- publikuje raporty z danymi w formacie PDF. Wydobycie z nich danych jest nietrywialne i kosztowne, bo nie da się tego na 100% zautomatyzować. W rezultacie prawie nikt nie powołuje się na WHO jako źródło danych -- lekki szejm przyznajmy, bo niby ta organizacja jest od tego żeby m.in. zbierać i udostępniać informację n/t.) Taka drobna różnica na początek: ECDC, OWiD oraz CSSE to prawdziwe bazy: zarejestrowane z dzienną częstotliwością zgony, przypadki, testy i co tam jeszcze. OWiD kopiuje dane z ECDC, kiedyś kopiowało z WHO ale napisali że WHO zawierało liczne błędy i to ich skłoniło do korzystania z ECDC (0:2 dla WHO). WMs publikuje stan na, bazy jako takiej nie ma (przynajmniej publicznie albo nie potrafię jej odszukać na stronie). Można założyć że jak się ogląda stronę WMs z ,,notowaniami'' nt/ koronawirusa w dniu X o godzinie T to jest to stan na X:T. Nawiasem mówiąc tak jest wygodniej, ale jednocześnie komplikuje to sprawę w aspekcie: dzienna liczba przypadków chociażby z uwagi na różnice czasu (jak w PL kończy się dzień to na Fiji jest w połowie inny; inna sprawa, że wątpię żeby ktoś się tym przejmował). Niemniej WMs ma rubrykę "nowe przypadki", tyle że nie bardzo wiadomo co to znaczy...

No więc po tym przydługim wstępie do rzeczy: jak się mają dane z WMs względem ECDC? Jak wspomniałem, na stronie WMs nie ma bazy -- jest tabela z danymi ze stanem ,,na teraz''. ECDC z kolei publikuje bazę w postaci arkusza kalkulacyjnego. Ściągam dane codziennie. Ze strony WMs o 21:00 (koniec dnia, przynajmniej w PL) oraz o 23:00 ze strony ECDC. Dane te wyglądają jakoś tak (WMs, po konwersji HTML→CSV):

date;country;totalC;newC;totalD;newD;totalT
04040600;USA;277467;+306;7402;+10;830095

Stempel czasu jest ustalany w momencie pobrania danych. Na stronie WMs czas nie jest podany explicite (nie ma czegoś takiego jak np. dane aktualizowano o H:M). Czyli 04040600 to dane z 2020/04/04 z godziny 6:00.

Dane ECDC wyglądają jakoś tak:

date;id;country;newc;newd;totalc;totald
2020-04-04;US;United_States_of_America;32425;1104;277965;7157

NewC -- nowe przypadki (dzienne); NewD -- nowe zgodny; totalC -- przypadki łącznie; totalD -- zgony łącznie. Baza ECDC ma stempel czasu (dzień).

W przypadku PL wiem, że Ministerstwo Zdrowia (MinZ) publikuje dane generalnie o godzinie 10-coś-tam oraz o 17/18-coś-tam. (Czemu tak nie wiem). Patrząc na dane z WMs wiedzę, że o 21:00 publikują już dane aktualne na ten dzień, w tym sensie, że uwzględnią stan z ostatniego dziennego komunikatu MinZ (ale jakiego formalnie dnia te dane dotyczą, to już inna sprawa, bo ten dzień przecież się nie skończył :-)). Jeżeli chodzi o ECDC to dane pobrane w dniu X zawierają dane do dnia X-1, żeby było śmieszniej ECDC dla tego dnia przypisuje dane z komunikatu MinZ z dnia X-2. Czyli na przykładzie: arkusz pobrany o 23:00 dnia 24/04/2020 będzie miał ostatni wiersz datowany 23/04 ale dane w tym wierszu będą tymi które pojawiły się na stronie MinZ w dniu 22/04.

Uzbrojony o tę wiedzę dla wybranych 24 krajów wykreśliłem dane (z kwietnia) w wersji WMs oraz ECDC, w dwóch wariantach: z oryginalnymi stemplami czasowymi (górny wiersz) oraz ze stemplem skorygowanym przy założeniu że dane ECDC są 24H opóźnione (czyli dzień 23/04 tak naprawdę to dzień 22/04 itd). Te ,,skorygowane dane'' to dolny wiersz. Dla 90% krajów dane łącznie nakładają się czyli dane są identyczne (są wyjątki--ciekawe czemu). Dane dzienne to misz-masz, każda baza ma własną wersję, nie wiadomo która jest prawdziwa, tyle, że ECDC ma zawsze dane dzienne a WMs niekoniecznie (dla Japonii prawie zawsze ta kolumna była pusta)

Dane i komplet wykresów jest tutaj

Poniżej kilka wybranych krajów:

poniedziałek, 13 kwietnia 2020

Wykresy typu Marimekko na przykładzie COVID19

Marimekko to zestawiony do 100% wykres słupkowy, gdzie szerokość słupka jest proporcjonalna do jego udziału w liczebności. (https://predictivesolutions.pl/wykres-marimekko-czyli-analityczny-patchwork)

Nie wiem o co chodzi, ale patrząc na przykłady, to jest to wykres pokazujący strukturę dwóch zmiennych na raz. Coś jak skumulowany wykres słupkowy (stacked barchart), tyle że słupki mają zmienną szerokość, odpowiadającą udziałom/liczebnościom wartości jednej cechy. Dla każdego słupka z kolei poszczególne segmenty mają wysokości proporcjonalne do udziałów/liczebności wartości drugiej cechy (w tym słupku). Alternatywną nazwą jest wykres mozaikowy.

Ale jest też trochę inny wariant takich wykresów, dla przypadku kiedy dla każdej jednostki w populacji generalnej jest Wi = Ci/Ni. Jeżeli wysokość słupka jest proporcjonalna do wartości Wi, szerokość jest proporcjonalna do Ni, to pola są oczywiście w proporcji Ci. Przykładowo jeżeli populacją generalną są kraje świata, wartością cechy Ni jest liczba mieszkańców w kraju i, wartością cechy Ci wielkość emisja CO2 w kraju i, to Wi jest oczywiście emisją per capita.

Moim zdaniem użyteczne, bo pokazuje na raz dwa natężenia: łączne, w skali całej populacji oraz szczegółowe, w skali jednostki. Kontynuując przykład: ile emituje przeciętny mieszkaniec kraju i oraz jaki jest udział emisji kraju i w całości emisji.

W przypadku epidemii COVID19 podstawową zmienną jest liczba zarażonych/zmarłych w kraju i. Ale jeżeli chcemy porównać kraj i z krajem j to oczywiście należy uwzględnić liczbę mieszkańców w obu krajach. Czyli wysokości słupków powinny odpowiadać liczbie zarażonych/zmarłych na jednostkę (np. na 1mln) a szerokości liczbie mieszkańców:

library(ggplot2)
library(dplyr)

dat <- "2020/04/09"

d <- read.csv("indcs.csv", sep = ';',  header=T, na.string="NA");
## liczba ludności w milionach (szerokość):
d$popm <- d$pop / million

## Oblicz współczynniki na 1mln
d$casesr <- d$cases/d$popm
### Wysokość:
d$deathsr <- d$deaths/d$popm

## Ograniczamy liczbę krajów żeby zwiększyć czytelność wykresu
## Tylko kraje wykazujące zmarłych
d <- d %>% filter(deaths > 0) %>% as.data.frame
## Tylko kraje z min 2/1mln i populacji > 1mln
d9 <- d %>% filter(deathsr > 2 & popm > 3 & deaths > 49) %>% droplevels() %>%
 arrange (deathsr) %>% as.data.frame

d9$w <- cumsum(d9$popm)
d9$wm <- d9$w - d9$popm
d9$wt <- with(d9, wm + (w - wm)/2)

d8$w <- cumsum(d8$popm)
d8$wm <- d8$w - d8$popm
d8$wt <- with(d8, wm + (w - wm)/2)

## Dzielimy etykiety na dwie grupy
## (inaczej wiele etykiet zachodzi na siebie)
d9$iso3h <- d9$iso3
d9$iso3l <- d9$iso3
## Kraje o niskich  wartościach bez etykiet
d9$iso3h[ (d9$popm < 15 ) ] <- ""
## Kraje o wysokich  wartościach bez etykiet
d9$iso3l[ (d9$popm >= 15 ) ] <- ""

p9  <- ggplot(d9, aes(ymin = 0)) +
  ylab(label="mratio (deaths/1mln)") +
  xlab(label="population (mln)") +
  ggtitle(sprintf("COVID19 mortality (%s | mratio > 2 | population > 3mln )", dat), 
      subtitle="source: https://www.ecdc.europa.eu/en/covid-19-pandemic (twitter.com/tprzechlewski)") +
  geom_rect(aes(xmin = wm, xmax = w, ymax = deathsr, fill = iso3)) +
  geom_text(aes(x = wt, y = 0, label = iso3h), vjust=+0.5, hjust=+1.25, size=2.0, angle = 90) +
  geom_text(aes(x = wt, y = 0, label = iso3l), vjust=+0.5, hjust=-0.20, size=1.5, angle = 90) +
  theme(legend.position = "none") 
  ## ... podobnie dla zmiennej casesr

Wynik w postaci rysunków:

Powyższe jest dostępne tutaj

niedziela, 12 kwietnia 2020

Więcej wykresów nt COVID19

Wymyśliłem sobie wykreślić wykresy pokazujące zależność pomiędzy liczbą zarażonych/zmarłych, a wybranymi wskaźnikami: GDP (zamożność), oczekiwana długość życia (poziom służby zdrowia) oraz śmiertelność dzieci (zamożność/poziom rozwoju). Większość danych pobrałem z portalu OWiD:

  • GDP (https://ourworldindata.org/economic-growth maddison-data-gdp-per-capita-in-2011us.csv)
  • LE (https://ourworldindata.org/life-expectancy life-expectancy.csv)
  • CM (https://ourworldindata.org/child-mortality child-mortality-igme.csv).

Dane dotyczące liczby ludności są z bazy Banku Światowego (https://data.worldbank.org/indicator/sp.pop.totl.) Dane dotyczące zarażonych/zmarłych ze strony ECDC (www.ecdc.europa.eu/en/covid-19/data-collection) Scaliłem wszystko do kupy skryptem Perlowym. NB pojawił się tzw. small problem, bo bazy OWiD oraz WorldBank używają ISO-kodów 3-literowych krajów, a ECDC dwuliterowych (PL vs POL). Trzeba było znaleźć wspólny mianownik. Szczęśliwie Perl ma gotowy moduł. Oprócz tego ECDC stosuje EU-standard w przypadku Grecji (EL zamiast GR) oraz Wielkiej Brytanii (UK/GB).

#!/usr/bin/perl
use Locale::Codes::Country;
...
while (<COVID>) { chomp();
($date, $iso2, $country, $newc, $newd, $totalc, $totald) = split /;/, $_;

   $iso3 = uc ( country_code2code($iso2, 'alpha-2', 'alpha-3'));
}

Rezultat jest zapisywany do pliku o następującej zawartości:

iso3;country;lex2019;gdp2016;cm2017;pop2018;cases;deaths
ABW;Aruba;76.29;NA;NA;105845;77;0
AFG;Afghanistan;64.83;1929;6.79;37172386;423;14
...

Wierszy jest 204, przy czym niektórym krajom brakuje wartości. Ponieważ dotyczy to krajów egzotycznych, zwykle małych, to pominę je (nie teraz później, na etapie przetwarzania R-em). Takich wybrakowanych krajów jest 49 (można grep-em sprawdzić). Jedynym większym w tej grupie jest Syria, ale ona odpada z innych powodów.

Do wizualizacji wykorzystam wykres punktowy (dot-plot) oraz wykresy rozrzutu (dot-plot).

library("dplyr")
library("ggplot2")
library("ggpubr")
## 
options(scipen=1000000)
## https://www.r-bloggers.com/the-notin-operator/
`%notin%` <- Negate(`%in%`)
##
today <- Sys.Date()
tt<- format(today, "%d/%m/%Y")
million <- 1000000
## Lista krajów Europejskich + Izrael
## (pomijamy kraje-liliputy)
ee <- c(
'BEL', 'GRC', 'LTU', 'PRT', 'BGR', 'ESP', 'LUX', 'ROU', 'CZE', 'FRA', 'HUN',
'SVN', 'DNK', 'HRV', 'MLT', 'SVK', 'DEU', 'ITA', 'NLD', 'FIN', 'EST', 'CYP',
'AUT', 'SWE', 'IRL', 'LVA', 'POL', 'ISL', 'NOR', 'LIE', 'CHE', 'MNE', 'MKD',
'ALB', 'SRB', 'TUR', 'BIH', 'BLR', 'MDA', 'UKR', 'ISR', 'RUS', 'GBR' );
ee.ee <- c('POL')

d <- read.csv("indcs.csv", sep = ';',  header=T, na.string="NA");
## Liczba krajów
N1 <- nrow(d)
## liczba ludności w milionach
d$popm <- d$pop / million

## Oblicz współczynniki na 1mln 
d$casesr <- d$cases/d$popm 
d$deathsr <- d$deaths/d$popm 

## Tylko kraje wykazujące zmarłych
d <- d %>% filter(deaths > 0) %>% as.data.frame
## Liczba krajów wykazujących zmarłych
N1d <- nrow(d)

## Tylko kraje z kompletem wskaźników (pomijamy te z brakami)
d <- d[complete.cases(d), ]

nrow(d)

##  UWAGA: pomijamy kraje o wsp. <= 2
##  droplevels() usuwa `nieużywane' czynniki
##  mutate zmienia kolejność na kolejność wg dearhsr:
d9 <- d %>% filter(deathsr > 2 ) %>% droplevels() %>% 
 mutate (iso3 = reorder(iso3, deathsr)) %>% as.data.frame

N1d2 <- nrow(d9)
M1d2 <- median(d9$deathsr, na.rm=T)

## https://stackoverflow.com/questions/11093248/geom-vline-with-character-xintercept
## Wykres punktowy
rys99 <- ggplot(d9, aes(x =iso3, y = deathsr )) +
  geom_point(size=1, colour = 'steelblue', alpha=.5) +
  xlab(label="Country") +
  ylab(label="Deaths/1mln") +
  ggtitle(sprintf("COVID19 mortality in deaths/mln (as of %s)", tt), 
   subtitle=sprintf("Countries with ratio > 0: %i | Countries with ratio > 2.0: N=%i (median %.1f)", 
       N1d, N1d2, M1d2)) +
  theme(axis.text = element_text(size = 4)) +
  ##theme(plot.title = element_text(hjust = 0.5)) +
  scale_y_continuous(breaks=c(0,20,40,60,80,100,120,140,160,180,200,220,240,260,280,300,320,340,360)) +
  geom_hline(yintercept=M1d2, linetype="solid", color = "steelblue") +
  geom_vline(aes(xintercept = which(levels(iso3) == 'POL')), size=1, color="#8A0303", alpha=.25) +
  geom_vline(aes(xintercept = which(levels(iso3) == 'DEU')), size=1, color="#8A0303", alpha=.25) +
  geom_vline(aes(xintercept = which(levels(iso3) == 'SWE')), size=1, color="#8A0303", alpha=.25) +
  coord_flip()

Uwaga: oś OX to skala porządkowa. Czynniki powinny być uporządkowane wg. wartości zmiennej z osi OY, czyli według deathsr. Do tego służy funkcja mutate (iso3 = reorder(iso3, deathsr). Można też uporządkować je ,,w locie'' aes(x =iso3, y = deathsr ), ale wtedy niepoprawnie będą kreślone (za pomocą geom_vline) linie pionowe. Linie pionowe służą do wyróżnienia pewnych krajów. Linia pozioma to linia mediany.

sources <- sprintf ("As of %s\n(Sources: %s %s)", tt,
  "https://www.ecdc.europa.eu/en/covid-19-pandemic",
  "https://ourworldindata.org/")

## Żeby etykiety nie zachodziły na siebie tylko dla wybranych krajów
## Add empty factor level! Istotne inaczej będzie błąd
# https://rpubs.com/Mentors_Ubiqum/Add_Levels
d$iso3 <- factor(d$iso3, levels = c(levels(d$iso3), ""))

d$iso3xgdp <- d$iso3
d$iso3xlex <- d$iso3
d$iso3xcm <- d$iso3

## Kraje o niskich  wartościach bez etykiet
## Bez etykiet jeżeli GDP<=45 tys oraz wskaźnik < 50:
d$iso3xgdp[ (d$gdp2016 < 45000) & (d$deathsr < 50 ) ] <- ""
## Inne podobnie
d$iso3xlex[ ( (d$lex2019 < 80) | (d$deathsr < 50 ) ) ] <- ""
d$iso3xcm[ ((d$cm2017 > 1.2) | (d$deathsr < 50 ) ) ] <- ""

## GDP vs współczynnik zgonów/1mln
rys1 <- ggplot(d, aes(x=gdp2016, y=deathsr)) + 
  geom_point() +
  geom_text(data=d, aes(label=sprintf("%s", iso3xgdp), x=gdp2016, y= deathsr), vjust=-0.9, size=2 ) +
  xlab("GDP (USD, Constant prices)") + 
  ylab("deaths/1mln") + 
  geom_smooth(method="loess", se=F, size=2) +
  ggtitle("GDP2016CP vs COVID19 mortality", subtitle=sources)

## Life ex vs współczynnik zgonów/1mln
rys2 <- ggplot(d, aes(x=lex2019, y=deathsr)) + 
  geom_point() +
  geom_text(data=d, aes(label=sprintf("%s", iso3xlex), x=lex2019, y= deathsr), vjust=-0.9, size=2 ) +
  xlab("Life expentancy") + 
  ylab("deaths/1mln") + 
  geom_smooth(method="loess", se=F, size=2) +
  ggtitle("Life expentancy vs COVID19 mortality", subtitle=sources)

## Child mortality vs współczynnik zgonów/1mln
rys3 <- ggplot(d, aes(x=cm2017, y=deathsr)) + 
  geom_point() +
  geom_text(data=d, aes(label=sprintf("%s", iso3xcm), x=cm2017, y= deathsr), vjust=-0.9, size=2 ) +
  xlab("Child mortality %") +
  ylab("deaths/1mln") + 
  geom_smooth(method="loess", se=F, size=2) +
  ggtitle("Child mortality vs COVID19 mortality", subtitle=sources)

## GDP vs Child mortality 
rys0 <- ggplot(d, aes(x=gdp2016, y=cm2017)) + 
  geom_point() +
  xlab("GDP (USD, Constant prices)") + 
  ylab("Child mortality") +
  geom_smooth(method="loess", se=F, size=2) +
  ggtitle("GDP2016CP vs Child mortality", subtitle=sources)

Jeszcze raz dla krajów Europejskich:

## Tylko kraje Europejskie:
d <- d %>% filter (iso3 %in% ee) %>% as.data.frame

d$iso3xgdp <- d$gdp2016
d$iso3xlex <- d$lex2019
d$iso3xcm <- d$cm2017
d$iso3xgdp[ d$iso3 %notin% ee.ee ] <- NA
d$iso3xlex[ d$iso3 %notin% ee.ee ] <- NA
d$iso3xcm[ d$iso3 %notin% ee.ee ] <- NA

rys1ec <- ggplot(d, aes(x=gdp2016, y=casesr)) + 
  geom_point() +
  geom_text(data=d, aes(label=sprintf("%s", iso3), x=gdp2016, y= casesr), vjust=-0.9, size=2 ) +
  geom_point(data=d, aes(x=iso3xgdp, y= casesr), size=2, color="red" ) +
  xlab("GDP (USD, Constant prices") + 
  ylab("cases/1mln") +
  geom_smooth(method="loess", se=F, size=2) +
  ggtitle("GDP2016CP vs COVID19 cases (Europe)", subtitle=sources)
  ... itd...

Wynik w postaci rysunków:

Powyższe jest dostępne tutaj

sobota, 4 kwietnia 2020

Google Community Mobility Reports

Google has launched a new website that uses anonymous location data collected from users of Google products and services to show the level of social distancing taking place in various locations. The COVID-19 Community Mobility Reports web site will show population data trends of six categories: Retail and recreation, grocery and pharmacy, parks, transit stations, workplaces, and residential. The data will track changes over the course of several weeks, and as recent as 48-to-72 hours prior, and will initially cover 131 countries as well as individual counties within certain states. (cf. www.google.com/covid19/mobility/.)

The raports contains charts and comments in the form: NN% compared to baseline (in six above mentioned categories) where NN is a number. It is assumed the number is a percent change at the last date depicted (which accidentaly is a part of a filename). So for example a filename 2020-03-29_PL_Mobility_Report_en.pdf contains a sentence `Retail & recreation -78% compared to baseline` which (probably) means that (somehow) registered traffic at R&R facilities was 22% of the baseline. Anyway those six numbers was extracted for OECD countries (and some other countries) and converted to CSV file.

The conversion was as follows: first PDF files was downloaded with simple Perl script:

#!/usr/bin/perl
# https://www.google.com/covid19/mobility/
use LWP::UserAgent;
use POSIX 'strftime';

my $sleepTime = 11;

%OECD = ('Australia' => 'AU', 'New Zealand' => 'NZ',
'Austria' => 'AT', 'Norway' => 'NO', 'Belgium' => 'BE',
'Poland' => 'PL', 'Canada' => 'CA', 'Portugal' => 'PT',
'Chile' => 'CL', 'Slovak Republic' => 'SK',
## etc ...
);

@oecd = values %OECD;

my $ua = LWP::UserAgent->new(agent => 'Mozilla/5.0', cookie_jar =>{});
my $date = "2020-03-29";

foreach $c (sort @oecd) {
   $PP="https://www.gstatic.com/covid19/mobility/${date}_${c}_Mobility_Report_en.pdf";

   my $req = HTTP::Request->new(GET => $PP);
   my $res = $ua->request($req, "${date}_${c}_Mobility_Report_en.pdf");

   if ($res->is_success) { print $res->as_string; }
   else { print "Failed: ", $res->status_line, "\n"; }
}

Next PDF files was converted to .txt with pdftotext. The relevant fragments of .txt files looks like:

  Retail & recreation
+80%

-78%
compared to baseline

So it looks easy to extract the relevant numbers: scan line-by-line looking for a line with appropriate content (Retail & recreation for example). If found start searching for 'compared to baseline'. If found retrieve previous line:

#!/usr/bin/perl
$file = $ARGV[0];

while (<>) {   chomp();
  if (/Retail \& recreation/ ) { $rr = scan2base(); }
  if (/Grocery \& pharmacy/ ) { $gp = scan2base(); }
  if (/Parks/ ) { $parks = scan2base(); }
  if (/Transit stations/ ) { $ts = scan2base(); }
  if (/Workplaces/ ) { $wps = scan2base(); }
  if (/Residential/ ) { $res = scan2base();
     print "$file;$rr;$gp;$parks;$ts;$wps;$res\n";
     last;  }
}

sub scan2base {
  while (<>) {
   chomp();
   if (/compared to baseline/) {  return ($prevline); }
   $prevline = $_;
  }
}

Extracted data can be found here.

piątek, 3 kwietnia 2020

Źródła danych nt #Covid19 podsumowanie (wersja 04/2020)

Danych nt COVID19 jest multum bo są traktowane jako treść promocyjna, przyciągająca klikających. Każda tuba medialna (gazeta/portal/telewizja) w szczególności publikuje dane nt

Niestety z faktu, że danych nt COVID19 jest multum niewiele wynika, bo wszystkie są do dupy, w sensie że są wątpliwej jakości, tj. zwykle sposób w jaki są gromadzone nie jest opisany. Nie wiadomo kto jest klasyfikowany jako zarażony COVID19, nie wiadomo kto jest klasyfikowany jako zmarły w wyniku zarażenia COVID19. Można się domyślać że klasyfikowany jako zarażony COVID19 jest ten komu wykonany słynny test (w większości wypadków, podobno nie zawsze); zmarły w wyniku zarażenia COVID19 jest ten, któremu lekarz wypisał świadectwo zgonu ze stosownym wpisem.

Powyższe skutkuje: niemożnością oceny prawdziwej skali zjawiska (stąd teorie że rząd fałszuje) oraz niemożnością dokonania wiarygodnych porównań międzynarodowych.

Jeżeli chodzi o Polskę, to nikt nie prowadzi publicznego rejestru. Strona GIS to w ogóle kuriozalnie wygląda. Są komunikaty, jak ktoś ma czas to może jest sobie z nich dane wydłubywać i agregować. Na poziomie międzynarodowym są 2 źródła agregacji pierwotnej nazwijmy to: WHO oraz ECDC. Te dwa źródła agregują dane nadsyłane przez ciała krajowe, wg jakiejś niezdefiniowanej (przypuszczalnie ad hoc ustalanej) procedury. Inni korzystają z danych WHO/ECDC pośrednio lub bezpośrednio ewentualnie uzupełniając/modyfikując je w bliżej niezdefiniowany sposób. No i są jeszcze źródła specyficzne takie jak Google Community Mobility Reports.

WHO Situation Reports. To nie jest baza danych, ale pliki PDF zawierające raporty w tym dane. Pozyskanie z nich danych wymaga nietrywialnej konwersji. www.who.int/emergencies/diseases/novel-coronavirus-2019/situation-reports . Dane z raportów dostępne są m.in. na stronie Wikipedii: en.wikipedia.org/wiki/2019%E2%80%9320_coronavirus_pandemic_cases/WHO_situation_reports oraz en.wikipedia.org/wiki/Talk:2019%E2%80%9320_coronavirus_pandemic_cases/WHO_situation_reports

ECDC.europa.eu Dane udostępniane w postacji codziennie aktualizowanego arkusza kalkulacyjnego. www.ecdc.europa.eu/en/covid-19/data-collection [Since the beginning of the coronavirus pandemic, ECDC's Epidemic Intelligence team has been collecting the number of COVID-19 cases and deaths, based on reports from health authorities worldwide.]

John Hopkins Univ/CSSE github.com/CSSEGISandData/COVID-19 [To identify new cases, we monitor various twitter feeds, online news services, and direct communication sent through the dashboard. Before manually updating the dashboard, we confirm the case numbers using regional and local health departments, namely the China CDC (CCDC), Hong Kong Department of Health, Macau Government, Taiwan CDC, European CDC (ECDC), the World Health Organization (WHO), as well as city and state level health authorities.]

Worldometers https://worldometers.info/coronavirus/ [nie wiadomo jak zbierane, przypuszczalnie kopiowane z WHO/ECDC; Worldometers, to -- wydaje się -- inicjatywa PR-owa firmy produkującej oprogramowanie]

OWiD czyli Our World in Data wykorzystuje bazę ECDC. ourworldindata.org/coronavirus-source-data [na podstawie ECDC]

Reasumując: jak ktoś potrzebuje gotowego zbioru danych, to ma do wyboru ECDC/OWiD/CSSE. Wszystkie są wątpliwe, ale lepszych nie ma a ci przynajmniej podają (ogólnikowo to fakt) jak te dane zbierają. Jak ktoś używa worldometers to pytanie czemu to robi... Jak posługuje się jeszcze innymi bardziej egoztycznymi danymi to szkoda tracić czasu na jego analizy (ew. sprawdzić czy nie są to dane ECDC/OWiD/CSSE tylko pod inną marką sprzedawane).

W Polsce nie ma oficjalnego rejestru. Przynajmniej ja nic nie wiem na temat. To tak nawiasem mówiąc szejm. Że żaden urząd, uniwersytet czy instytut nie udostępnia oficjalnych/wiarygodnych/kompletnych/łatwo dostępnych danych (w Niemczech na przykład robi to słynny RKI; a we Francji nie mniej słynny pasteur.fr). W PL zaś każdy się stara i coś tam udostępnia, z naciskiem na coś... Znalazłem rejestr nieopisany (w sensie jak/skąd są nim gromadzone dane) prowadzony przez dziennik z grupy PolskaPress. dziennikzachodni.carto.com/tables/zachorowania_na_koronawirusa_w_polsce_marzec/public

Google Community Mobility Reports To nie jest baza danych, ale zbiór raportów w formacie PDF. www.google.com/covid19/mobility/. [Google has launched a new website that uses anonymous location data collected from users of Google products and services to show the level of social distancing taking place in various locations. The COVID-19 Community Mobility Reports web site will show population data trends of six categories: Retail and recreation, grocery and pharmacy, parks, transit stations, workplaces, and residential. The data will track changes over the course of several weeks, and as recent as 48-to-72 hours prior, and will initially cover 131 countries as well as individual counties within certain states.] Ciekawostka raczej, bo w szczególności, nie do końca wiadomo co te procenty Gógla oznaczają, np. -60% względem baseline. Anie nie wiadomo co to jest ten baseline (średnia?) ani jak liczony jest ruch...

Nie mniej wydłubałem te procenty z raportów dla krajów OECD i zamieniłem na plik w formacie CSV. Jest on do pobrania tutaj.

Dane dotyczące USA. Oczywiście są częścią WHO/ECDC/CSSE. Ale są także bardziej szczegółowe:

CDC [The provisional counts for coronavirus disease (COVID-19) deaths are based on a current flow of mortality data in the National Vital Statistics System.] https://www.cdc.gov/nchs/nvss/vsrr/COVID19/index.htm

NewYork Times [The data is the product of dozens of journalists working across several time zones to monitor news conferences, analyze data releases and seek clarification from public officials on how they categorize cases.] https://github.com/nytimes/covid-19-data oraz https://www.nytimes.com/interactive/2020/us/coronavirus-us-cases.html

No i jeszcze są pewnie jakieś chińskie dane, ale to trzeba znać chiński.

środa, 1 kwietnia 2020

Czujnik temperatury/wilgotności/ciśnienia Bosch BME 280

Czujnik ten jest fragmentem większej całości, że tak powiem tajemniczo. Do jego uruchomienia wykorzystałem doskonały opis ze strony https://twojpomyslna.wordpress.com/2019/02/18/raspberry-pi-bme280-i2c-domoticz/. Zresztą jest to ekstremalnie proste...

BME 280 występuje w kilku wariantach. Na stronie twojpomyslna.wordpress.com pokazany jest czujnik z czterema stykami, a ja kupiłem z sześcioma i też działa. Styki podłączamy następująco: VCC→#P1, GND→#P6, SCL→#P5, SDA→#P3.

Potem należy zainstalować stosowny software:

sudo apt-get install -y python-smbus i2c-tools
## Sprawdzamy czy działa
## ls -l /dev/i2c*
## crw-rw---- 1 root i2c 89, 1 mar 29 15:41 /dev/i2c-1
i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

Teraz ściągam/uruchamiam skrypt bme280.py:

wget https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/bme280.py
python bme280.py

Wydruk jest dość rozwlekły i bez daty/czasu więc modyfikuję skrypt, tak aby całość była drukowana w jednym wierszu. Dodaję stosowany wpis do crontaba.

Czujnik jest lepszy od DHT22 nie tylko dlatego, że oprócz temperatury/wilgotności, jeszcze mierzy ciśnienie, ale także działa podłączony kilkumetrowym przewodem a DHT22 odmawiał wtedy współpracy.

Korespondencyjne wybory w Bawarii

Przez przypadek ciekawe odkrycie. W niedzielę zakończyły się wybory w Bawarii, wyłącznie w trybie korespondencyjnym z uwagi na epidemię #COVID19. Jednocześnie w PL trwa wałkowanie tematu pn przesunąć wybory prezydenckie. Niewątpliwie przykład niemiecki to kłopot, że tak powiem narracyjny, dla tych co chcą przesunięcia.

No więc naiwnie wpisałem w google: bavaria+second+round+elections+postal a w rezultacie dostałem głównie strony o apelu kandydatki Kidawy o przesunięcie wyborów w tym kuriozalna relacja Reutersa -- kiedyś szanowanej agencja informacyjnej, teraz kandyjskiej dezinformacyjnej prop-tuby. W dziale "Zdrowie" -- a jakże -- donosi ona o apelu o bojkot p. Kidawy kończąc ten "zdrowotny raport" raportem pana J. Flisa z Krakowa (tak to ten sam, udający naukowca, telewizyjny-profesor Flis), ale na temat wyborów w Bawarii, które skutkowały wg. p. Flisa 2 tys ofiar (metody wyliczeń, którą posłużył się "profesor" nie podano). Zaistne niezwykle relewantny dokument do mojego zapytania.