wtorek, 28 lipca 2015

Spływ Krutynią

W zeszłą niedzielę przepłynąłem z Elką fragment Krutyni, konkretnie od rezerwatu koło wsi Krutyń do mostu we wsi Ukta (circa 15 km w czasie 3 godzin bez minuty). Opis trasy można znaleźć m.in. na stronach firmy Turystyka Aktywna Wodniak z której usług korzystaliśmy i możemy ją polecić jako solidną. Wypożyczenie kajaka kosztowało 55 PLN czyli tanio.

Miejscem startu było w naszym wypadku Jezioro Krutyńskie (rezerwat Krutynia), do którego zawiózł nas mikrobus Wodniaka. Bus nie może podjechać do samej wody (rezerwat!) więc kajak trzeba 50 m donieść samemu (albo dać zarobić 2 PLN miejscowym chłopakom, którzy przewiozą go wózkiem). Na trasie spływu jest jedna przenioska przy młynie wodnym w miejscowości Zielony Lasek, ale tam też są chłopcy z wózkami (koszt usługi tym razem 5 PLN).

Ślad całej wycieczki (ze zdjęciami) jest też tutaj.

poniedziałek, 13 lipca 2015

Głupi i głupszy: występ w Berlinie

Człowiek z obrazka (ten po prawej) znowy błysnął -- tym razem w Berlinie:

"Czyn Clausa von Stauffenberga, który targnął się na życie samego Hitlera, to ważne ogniwo europejskiego ruchu oporu -- podobnie jak... powstanie warszawskie" -- stwierdził Bronisław Komorowski na wykładzie wygłoszonym w ostatnim tygodniu podczas pożegnalnej podróży do Niemiec.

Jego występ był nie do strawienie nawet dla życzliwych mu mediów, jeszcze w maju agitujących za "najlepszym prezydentem 25 lecia". Newsweek odnosząc się do cytatu z akapitu wyżej: "Przesadził? Raczej tak..." Polityka udaje, że zdarzenia nie było -- no cóż nawet prof. Władyka miałby kłopoty z interpretacją historii wg. Bredzisława. Jedynie GłosCadyka, idąc w zaparte, broni swojego człowieka roku 2014: wszyscy w Niemczech byli brunatni, więc należy docenić, że ktoś był mniej brunatny niż ktoś inny (B.T. Wieliński w tekście: Prezydent Komorowski jedzie do Berlina. Prawica grzmi, że uczci Stauffenberga -- wroga Polski).

Patrona broni też głupszy:

Brak szacunku dla takiej osoby jest brakiem szacunku dla Polaków, którzy w walce z Hitlerem zginęli...

Wszyscy się okazuje walczyli z Hitlerem--jakimś cyborgiem, co sam jeden podbił pół Europy. Nałęcz udając głupiego, i zamiast Niemcy mówiąc Hitler, posługuje się prostacką propagandową kalką: całe zło to Hitler, a reszta systemu, w tym Wehrmacht -- no cóż niewiele mogli, a niektórzy bohatersko walczyli...

Całe szczęście, że Szef i jego dwór odchodzą w niebyt już za 24 dni.

środa, 8 lipca 2015

Konwersja Excela na CSV

Jak to z Microsoftem bywa nie jest łatwo. Są dwa formaty Excela -- stary (.xls) oraz nowy (.xlsx). Pakiety Perlowe Spreadsheet::Excel oraz Spreadsheet::ParseXLSX radzą sobie nieźle, aczkolwiek oczywiście gwarancji nie ma i być nie może skoro sam Excel czasami siebie samego nie potrafi zinterpretować.

No ale jest jeszcze trzeci format: jak plik .xlsx jest zabezpieczone hasłem (password protected). I na taką okoliczność nie ma zbyt wielu narzędzi. Można wszakże problem rozwiązać w dwóch krokach korzystając Libreoffice, który potrafi interpretować pliki Excela i można go uruchomić w trybie batch:

#!/bin/bash
XLS="$1"
TMP="${XLS%.*}.xlsx"
libreoffice --headless --convert-to xlsx "$XLS" --outdir ./xlsx-temp/
perl xslx2csv.pl ./xlsx-temp/"$TMP" "$OUTFILE"

Powyższy skrypt obsłuży wszystkie rodzaje plików Excela, zamieniając je najpierw na plik w formacie XLSX (plik password protected zostanie zmieniony na prawdziwy format XLSX, interpretowalny przez np. Spreadsheet::ParseXLSX).

Można od razu konwertować do CSV (--convert-to csv), ale konwersji będzie podlegać tylko pierwszy arkusz. Jak interesuje nas na przykład drugi, to kicha... nie da się (a przynajmniej ja nie wiem jak to osiągnąć). Inny problem to zamiana XLSX→XLSX -- nie ma w LibreOffice możliwości określenia nazwy pliku wynikowego, a próba:

libreoffice --headless --convert-to xlsx plik.xlsx

Kończy się błędem. Na szczęście jest obejście w postaci opcji --outdir. Plik wyjściowy -- o tej samej nazwie co wejściowy -- jest zapisywany w innym katalogu i problem rozwiązany.

Po zamianie Excela na ,,kanoniczny'' XLSX do konwersji na CSV można wykorzystać następujący skrypt Perla:

#!/usr/bin/perl
# Wykorzystanie perl xslx2csv.pl plik.xslx [numer-arkusza]

use Spreadsheet::ParseXLSX;
use open ":encoding(utf8)";
use open IN => ":encoding(utf8)", OUT => ":utf8";

$xslxfile = $ARGV[0]; 
$ArkuszNo = $ARGV[1] || 1; ## domyślnie arkuszu 1

my $source_excel = new Spreadsheet::ParseXLSX;
my $source_book = $source_excel->parse("$xslxfile")
  or die "Could not open source Excel file $xslxfile: $!";

# Zapisuje zawartość wybranego arkusza do hasza %csv
my %csv = ();

foreach my $sheet_number (0 .. $source_book->{SheetCount}-1) {
  my $sheet = $source_book->{Worksheet}[$sheet_number];

  print STDERR "*** SHEET:", $sheet->{Name}, "/", $sheet_number, "\n";
  if ( $ArkuszNo ==  $sheet_number + 1 ) {

    next unless defined $sheet->{MaxRow};
    next unless $sheet->{MinRow} <= $sheet->{MaxRow};
    next unless defined $sheet->{MaxCol};
    next unless $sheet->{MinCol} <= $sheet->{MaxCol};

    foreach my $row_index ($sheet->{MinRow} .. $sheet->{MaxRow}) {
       foreach my $col_index ($sheet->{MinCol} .. $sheet->{MaxCol}) {
          my $source_cell = $sheet->{Cells}[$row_index][$col_index];
	  if ($source_cell) {
	    $csv{$row_index}{$col_index} = $source_cell->Value;
	  }
       }
    }
  }
}

Arkusz jest w haszu %csv. Jak go przekształcić/wydrukować itp. pozostawiam inwencji ewentualnego czytelnika.

Wysyłanie posta na blogger.com z wykorzystaniem Google API

GoogleCL przestało działać, bo Google przestało obsługiwać wersję OAuth 1.0. Ponadto, wygląda na to, że dostosowanie tego użytecznego narzędzia do wersji OAuth 2.0 bynajmniej nie jest trywialne na co wskazują liczne (ale do tej pory bezskuteczne) prośby i wołania o aktualizację GoogleCL, które można znaleźć w Internecie.

Ponieważ poszukiwania w miarę podobnego zamiennika zakończyły się niepowodzeniem, nie pozostało nic innego zmajstrować coś samodzielnie. Autoryzację OAuth 2.0 mam już opanową -- obsługuje ją Pythonowy skrypt oauth2picasa.py. (Skrypt jest (zapożyczonym) fragmentem z projektu picasawebsync). Wystarczyło dorobić następujący prosty skrypt Perlowy (por. także: Publishing a blog post):

#!/usr/bin/perl
# *** Wyslanie posta na blogger.com ***
use strict;
use LWP::UserAgent;
use XML::LibXML;
use Getopt::Long;

my $profileID="default";
my $blogID = '1928418645181504144'; # Identyfikator bloga
my $blog_entry ;

## Na wypadek gdy ktoś ma kilka blogów moża podać na któr
## ma być wysłany post używając opcji -blog
GetOptions( "blog=s" => \$blogID, "post=s" => \$blog_entry) ;

if ( $blog_entry eq '' ) {
print STDERR "*** USAGE: $0 -b blog -p message (-b is optional) ***\n" }

## sprawdź czy post jest well formed:
my $parser = XML::LibXML->new();
eval {my $res_  = $parser->parse_string($blog_entry) };
if ($@) { die "*** Error parsing post message! \n"; }

my $ACCESS_TOKEN=`oauth2blogger.py`; # pobierz ACCESS_TOKEN
print STDERR "*** AccessToken: $ACCESS_TOKEN ***\n";

my $req = HTTP::Request->new(
  POST => "https://www.blogger.com/feeds/$blogID/posts/default");

$req->header( 'Content-Type' => 'application/atom+xml' );
$req->header( 'Authorization' => "Bearer $ACCESS_TOKEN" );
$req->header( 'GData-Version' => '2' );

$req->content($blog_entry);

my $ua = LWP::UserAgent->new;
my $res = $ua->request($req);

# Jeżeli coś jest nie tak poniższe drukuje verbatim:
# http://www.perlmonks.org/bare/?node_id=464442
# $ua->prepare_request($req); print($req->as_string); exit ;

if ($res->is_success) {
   my $decoded_response = $res->decoded_content;
   print STDERR "*** OK *** $decoded_response\n"; }
else { die $res->status_line; }

Wykorzystanie:

perl blogger_upload.pl -p 'treść-posta'

Treść posta musi być oczywiście w formacie xHTML i zawierać się wewnątrz elementu content, który z kolei jest wewnątrz elementu entry. Element entry zawiera także title określający tytuł posta, oraz elementy category zawierające tagi. Przykładem może być coś takiego:

<entry xmlns='http://www.w3.org/2005/Atom'>
 <title type='text'>Marriage!</title>
 <content type='xhtml'>
    <div xmlns="http://www.w3.org/1999/xhtml">
      <p>Mr. Darcy has proposed marriage to me!</p>
      <p>He is the last man on earth I would ever desire to marry.</p>
      <p>Whatever shall I do?</p>
    </div>
  </content>
  <category scheme="http://www.blogger.com/atom/ns#" term="marriage" />
  <category scheme="http://www.blogger.com/atom/ns#" term="Mr. Darcy" />
</entry>

Opisany skrypt jest tutaj: blogger_upload.pl.

wtorek, 23 czerwca 2015

Mój nowy PCet

Intel NUC/Akasa case
Intel NUC/Obudowa Akasa

Niedawno zdecydowałem się na upgrade i stanęło na Intel NUC, konkretnie model D54250WYKH wyposażony w procesor Core i5-4250U/Haswell (nieobciążony pobiera podobno 6 watów!). Jak wiadomo NUC sprzedawany jest jako tzw. kadłubek/barebone zatem trzeba samodzielnie dokupić i zamontować dysk oraz pamięć RAM. Intel coś tam rekomenduje i jest dostępna na stosownej stronie (URL tej strony można znaleźć w kartonie z NUCem) lista zweryfikowanego sprzętu, ale ja wybrałem coś czego na liście nie było: pamięć 2x Kingston SODIMM PC-1600 DDR3L 4GB CL11 KVR16LS11/4 oraz dysk SSD Samsung 850 EVO (500 Gb) z interfejsem SATA bo taki miałem (domyślnym jest mSATA, z tym że obudowa D54250WYKH jest na tyle duża, że pozwala na instalację dwóch dysków, a system może być uruchamiany z obu).

NUC ma w środku wiatrak i do tego ten wiatrak całkiem wyraźnie słychać -- być może jestem przewrażliwiony bo są też opinie w stylu: jest praktycznie niesłyszalny gdy pracuje. Usiłowałem zmniejszyć prędkość wiatraka poprzez stosowne ustawienia BIOSa (zmiana control mode na manual plus zmniejszenie duty cycle z fabrycznych 40% na 30%, por. Intel NUC DN2820FYKH Bay Trail System Review) i wtedy faktycznie komputer działał ciszej, ale temperatura procesora (pod obciążeniem) potrafiła wzrosnąć do 85 stopni.

Nie po to kupowałem dysk SSD żeby coś mi się tam kręciło -- za następne 300 PLN kupiłem obudowę Akasa Newton X fanless case. Obudowa jest większa od oryginalnej i znacząco cięższa (1,3 kg wg producenta), ale faktycznie pozwala na wyłączenie wiatraka (podczas przekładania płyty do nowej obudowy wiatrak został odkręcony zresztą). Temperatura procesora jaką odnotowałem nigdy nie była wyższa od 55 stopni a zwykle znajduje się w przedziale 35--45 stopni.

Z instalacją Fedory 21 -- bo takiej wersji Linuksa używam, nie było żadnych problemów (z dokładnością do błędów Fedory). Aby móc dalej korzystać z wiekowej drukarki HP6P kupiłem na Allegro za całe 9,90 PLN przejściówkę pn. konwerter kabel USB 2.0 wtyk--LPT gniazdo DB25 ponieważ NUC nie posiada gniazda LPT/IEEE 1284. Konwerter zadziałał od pierwszego strzału, co było miłym zaskoczeniem.

Drobny problem wystąpił z równie wiekowym skanerem Canon LIDE 25, który wieszał się po zeskanowaniu jednej strony. Się okazało, że powodem jest błąd w programie XSane/sane-backends w wersji 1.0.24. Rozwiązaniem było ściągnięcie z githuba i skompilowanie wersji 1.0.25.

Recenzji NUCa jest w Internecie multum, np. tutaj.

Wysyłanie plików na Picasaweb za pomocą Curla

Taki oto (uproszczony) skrypt basha używałem do niedawna do wysyłania plików na konto Picasaweb (por. Using cURL to interact with Google Data services oraz PicasaUploader):

USERNAME=SomeUsername # GoogleAccountUsername
PASSWORD=SomePasswd # GoogleAccountPasswd
ALBUM_ID=6008849823888405298 # ID of picasaweb album

MY_PIC="$1" ## filename of the picture to upload ##
PIC_TITLE=`basename $MY_PIC` # filename w/o extension
PIC_TYPE=`file -b --mime-type "$MY_PIC"`

AUTH_KEY=$( curl -s https://www.google.com/accounts/ClientLogin -d Email="$USERNAME"@gmail.com \
  -d Passwd="$PASSWORD" -d accountType=GOOGLE \
  -d source=Google-cURL-Example -d service=lh2 | awk -F\= '/^Auth=/{print $2}' )
ALBUM_XML="http://picasaweb.google.com/data/feed/api/user/$USER_ID/albumid/$ALBUM_ID?authkey=$AUTH_KEY"

URL=`curl -s --request POST --data-binary "@$MY_PIC" --header "Slug: $PIC_TITLE" \
 --header "Content-Type: $PIC_TYPE" \
 --header "Authorization: GoogleLogin auth=$AUTH_KEY" "$ALBUM_XML" | \
sed 's/.*media:content url='"'"'\([^'"'"']*\).*media:thumbnail url='"'"'\([^'"'"']*\).*/\1/'`

Zmienna URL zawiera url wysłanego na Picasaweb obrazka (otrzymany przez zaaplikowanie w potoku odpowiedniego skryptu seda).

Skrypt przestał działać ostatnio, ponieważ Google nie obsługuje już protokołu OAuth 1.0 (por. Google Identity Platform). Na szczęście w łatwy sposób możliwe było dopasowanie skryptu nowszego protokołu OAuth 2.0:

ACCESS_TOKEN=$(oauth2picasa.py) ## Acces token is managed with Python's script now
## Note that ALBUM_XML URL starts now from https:// now
ALBUM_XML="https://picasaweb.google.com/data/feed/api/user/$USER_ID/albumid/$ALBUM_ID"

URL=`curl -s --request POST --data-binary "@$MY_PIC" \
--header "GData-Version: 2" --header "Slug: $PIC_TITLE" \
--header "Content-Type: $PIC_TYPE" -H "Authorization: Bearer $ACCESS_TOKEN" $ALBUM_XML |  \
sed 's/.*media:content url='"'"'\([^'"'"']*\).*media:thumbnail url='"'"'\([^'"'"']*\).*/\1/'`

Uwaga: Autoryzację OAuth 2.0 obsługuje Pythonowy skrypt oauth2picasa.py. Skrypt jest (zapożyczonym) fragmentem z projektu picasawebsync:

#!/usr/bin/python

import os
import time
import httplib2
## https://github.com/google/oauth2client
## installed with pip install --upgrade oauth2client (or some other way)
from oauth2client import client

def oauthLogin():
        # using http://stackoverflow.com/questions/20248555/list-of-spreadsheets-gdata-oauth2/29157967#29157967
        from oauth2client.file import Storage

        filename = os.path.join(os.path.expanduser('~'), ".picasawebsync")
        client_secrets = os.path.join(os.path.expanduser('~'), ".config", "picasawebsync.json")

 storage = Storage(filename)
        credentials = storage.get()
        if credentials is None or credentials.invalid:  
         flow = client.flow_from_clientsecrets(client_secrets,
         scope='https://picasaweb.google.com/data/',
         redirect_uri='urn:ietf:wg:oauth:2.0:oob')     
                auth_uri = flow.step1_get_authorize_url()       
                print 'Authorization URL: %s' % auth_uri
                auth_code = raw_input('Enter the auth code: ')
                credentials = flow.step2_exchange(auth_code)
                storage.put(credentials)
        if credentials.access_token_expired:
                credentials.refresh(httplib2.Http())
                
        return credentials.access_token

# start of the program

gd_client = oauthLogin()

print '%s' % gd_client

Jak stworzyć OAuth 2.0 client ID opisano tutaj. Należy wybrać Client ID for native application. Następnie należy pobrać plik JSON, zmienić nazwę tego pliku na picasawebsync.json umieszczając go w katalogu .config swojego katalogu domowego (albo zmodyfikować skrypt, jeżeli plik ma być w innym miejscu).

Uruchomiony po raz pierwszy skrypt oauth2picasa.py wypisuje URL, który należy skopiować/wkleić do przeglądarki, celem uzyskania authcode. Pobrany authcode wklejamy w odpowiedzi na prompt Enter the auth code:.

Opisane skrypty są tutaj: picasa_upld.sh oraz oauth2picasa.py.

poniedziałek, 22 czerwca 2015

Uploading pictures to Picasaweb with Curl

My (simplified) old bash script for uploading images to Picasaweb (cf. Using cURL to interact with Google Data services and PicasaUploader):

USERNAME=SomeUsername # GoogleAccountUsername
PASSWORD=SomePasswd # GoogleAccountPasswd

MY_PIC="$1" ## filename of the picture to upload ##
ALBUM_ID=6008849823888405298 # ID of picasaweb album
PIC_TITLE=`basename $MY_PIC` # filename w/o extension
PIC_TYPE=`file -b --mime-type "$MY_PIC"`

AUTH_KEY=$( curl -s https://www.google.com/accounts/ClientLogin -d Email="$USERNAME"@gmail.com \
  -d Passwd="$PASSWORD" -d accountType=GOOGLE \
  -d source=Google-cURL-Example -d service=lh2 | awk -F\= '/^Auth=/{print $2}' )
ALBUM_XML="http://picasaweb.google.com/data/feed/api/user/$USER_ID/albumid/$ALBUM_ID?authkey=$AUTH_KEY"

URL=`curl -s --request POST --data-binary "@$MY_PIC" --header "Slug: $PIC_TITLE" \
 --header "Content-Type: $PIC_TYPE" \
 --header "Authorization: GoogleLogin auth=$AUTH_KEY" "$ALBUM_XML" | \
sed 's/.*media:content url='"'"'\([^'"'"']*\).*media:thumbnail url='"'"'\([^'"'"']*\).*/\1/'`

URL contains url of uploaded picture (obtained with piping curl's output via sed).

This script stoped working recently as Google no longer supports OAuth 1.0 (cf. Google Identity Platform). Fortunately it was pretty easy to modify it to support OAuth 2.0:

ACCESS_TOKEN=$(oauth2picasa.py) ## Acces token is managed with Python's script now
## Note that ALBUM_XML URL starts now from https:// now
ALBUM_XML="https://picasaweb.google.com/data/feed/api/user/$USER_ID/albumid/$ALBUM_ID"

URL=`curl -s --request POST --data-binary "@$MY_PIC" \
--header "GData-Version: 2" --header "Slug: $PIC_TITLE" \
--header "Content-Type: $PIC_TYPE" -H "Authorization: Bearer $ACCESS_TOKEN" $ALBUM_XML |  \
sed 's/.*media:content url='"'"'\([^'"'"']*\).*media:thumbnail url='"'"'\([^'"'"']*\).*/\1/'`

NOTE: OAuth 2.0 authorization is handled with tiny Python script oauth2picasa.py. The script is an adapted/copy-pasted fragment of code borrowd from picasawebsync:

#!/usr/bin/python

import os
import time
import httplib2
## https://github.com/google/oauth2client
## installed with pip install --upgrade oauth2client (or some other way)
from oauth2client import client

def oauthLogin():
        # using http://stackoverflow.com/questions/20248555/list-of-spreadsheets-gdata-oauth2/29157967#29157967
        from oauth2client.file import Storage

        filename = os.path.join(os.path.expanduser('~'), ".picasawebsync")
        client_secrets = os.path.join(os.path.expanduser('~'), ".config", "picasawebsync.json")

 storage = Storage(filename)
        credentials = storage.get()
        if credentials is None or credentials.invalid:  
         flow = client.flow_from_clientsecrets(client_secrets,
         scope='https://picasaweb.google.com/data/',
         redirect_uri='urn:ietf:wg:oauth:2.0:oob')     
                auth_uri = flow.step1_get_authorize_url()       
                print 'Authorization URL: %s' % auth_uri
                auth_code = raw_input('Enter the auth code: ')
                credentials = flow.step2_exchange(auth_code)
                storage.put(credentials)
        if credentials.access_token_expired:
                credentials.refresh(httplib2.Http())
                
        return credentials.access_token

# start of the program

gd_client = oauthLogin()

print '%s' % gd_client

How to create an OAuth 2.0 client ID in the Google Developers Console is described here. Choose Client ID for native application. Next download JSON file, rename it to picasawebsync.json and move it to .config directory in your HOME directory (or modify the script).

First time used oauth2picasa.py prompts to sign on to google and paste the link back to authenticate.

Source code: picasa_upld.sh and oauth2picasa.py.

środa, 27 maja 2015

My old Canon LIDE 25 scanner works only once

My old Canon CanoScan LIDE 25 scanner is not working properly with my new hardware (Intel NUC) and Fedora 21.

I suspected Fedora 21 bug, but the problem is with the hardware, exactly as describe here. Works once and got stuck when scanning next.

It is claimed problem is solved in version 1.025 of Sane. To compile Sane one has to install libusb-devel package first:

yum install libusb-devel

Then:

git clone git://git.debian.org/sane/sane-backends.git
cd sane-backends

# I would like to install whole stuff in /usr/local
# without removing original fedora package
./configure --prefix=/usr/local --sysconfdir=/usr/local/etc \
    --localstatedir=/var BACKENDS=plustek
make
make install

Sane works now.

I would like to avoid mixing original Sane files with the new ones, so I use --prefix and --sysconfdir options with appropriate values. New Sane will be installed in /usr/local and it will be run by default--no need to remove original Fedora packages.

wtorek, 26 maja 2015

Aksjomat Balcerowicza: im większe wpływy związków zawodowych, tym mniej miejsc pracy


TU density vs GDP

TU density vs emp. rate

TU density vs unemp. rate

Kontunuując minianalizę rozpoczętą w poprzednim wpisie, a dotyczącą zależności pomiędzy zatrudnieniem a uzwiązkowieniem (w związku ze śmiałą tezą L. Balcerowicza, że taka zależność istnieje i jest ujemna):

require(ggplot2)

## https://stats.oecd.org/Index.aspx?DataSetCode=UN_DEN
## http://stats.oecd.org/Index.aspx?DatasetCode=STLABOUR
## employment rate Q42012
d <- read.csv("union_density_and_gdp.csv", sep = ';',  header=T, na.string="NA");

## tu.density = ratio of  wage and salary earners
## that are trade union members, divided by the total number of wage and salary earners:
## gdppc = GDP per capita
ggplot(d, aes(d$tu.density, d$gdppc)) + geom_point() +
  geom_text(aes(label=d$iso),size=2.0, vjust=-0.35)  +
  xlab("TU density (%)") + ylab("GDPpc (tys USD)") +
  scale_colour_discrete(name="") +
  geom_smooth(method="lm", se=T, size=2)

lm <- lm(data=d, gdppc ~ tu.density ); summary(lm);

## employment rate vs tu.density:
ggplot(d, aes(d$tu.density,d$emprate)) + geom_point() +
  geom_text(aes(label=d$iso),size=2.0, vjust=-0.35)  +
  xlab("TU density (%)") + ylab("Empolyment rate (%)") +
  scale_colour_discrete(name="") +
  geom_smooth(method="lm", se=T, size=2);

lm <- lm(data=d, emprate ~ tu.density ); summary(lm);

## youth unemployment rate vs tu.density:
## http://www.oecd-ilibrary.org/employment/youth-unemployment-rate_20752342-table2
ggplot(d, aes(d$tu.density,d$yur)) + geom_point() +
  geom_text(aes(label=d$iso),size=2.0, vjust=-0.35)  +
  xlab("TU density (%)") + ylab("Youth unempolyment rate (%)") +
  scale_colour_discrete(name="") +
  geom_smooth(method="lm", se=T, size=2);

lm <- lm(data=d, yur ~ tu.density ); summary(lm)

Prosta regresja daje następujące rezultaty: zależność #1 pomiędzy GDP per capita a Trade Union Density jest słabo dodatnia (to już wiemy); zależność #2 pomiędzy współczynnikiem zatrudnienia a Trade Union Density też jest słabo dodatnia; zależność #3 pomiędzy stopą bezrobocia w grupie wiekowej 15--24 lat a Trade Union Density jest wprawdzie ujemna, ale statystycznie nieistotna (współczynnik $R^2$ do tego równy 1,4%).

Jak to wygląda graficznie widać na wykresach obok.

Zbiór danych jest do pobrania tutaj.

BTW: do konwersji pliku PDF na JPG wykorzystano:

convert -density 150 Rplots.pdf Rplots_%02d.png

Uwaga na koniec: zapis method="lm" jest bardziej poprawny niż method=lm zastosowany w poprzednim wpisie.

niedziela, 17 maja 2015

Im większe wpływy związków zawodowych, tym mniej miejsc pracy

Im większe wpływy związków zawodowych, tym mniej miejsc pracy
TU density vs GDP (OECD countries)
TU density vs GDP

Pan profesor Balcerowicz na finiszu kampanii prezydenckiej baaardzo mocno się zaangażował po stronie rządzącego układu, a to zaangażowanie przejawia się m.in. wzmożonym wypisaniem na Twitterze różnych mniej lub bardziej mądrych (zwykle mniej) sloganów (aka farmazonów). Np. "S" już poparła Dudę, który zabiega o poparcie OPZZ -- to zła wiadomość dla młodych. Im większe wpływy ZZ w państwie, tym mniej miejsc pracy..

Co szkodzi sprawdzić empirycznie tezę profesora? Pobrałem zatem ze strony stats.oecd.org dane dotyczące Trade Union Density (ratio of wage and salary earners that are trade union members, divided by the total number of wage and salary earners tj. udział fundusza płac związkowców do płac ogółem). A ze strony List of OECD countries by GDP per capita dane dotyczące GDP per capita (jakoś nie mogłem szybko odszukać tych liczb na stronie stats.oecd.org -- zakładam, że na wikipedia.org przepisano je bez błędów:-) Dane są z roku 2012.

require(ggplot2)

## https://stats.oecd.org/Index.aspx?DataSetCode=UN_DEN
d <- read.csv("union_density_and_gdp.csv", sep = ';',  header=T, na.string="NA");

ggplot(d, aes(d$tu.density,d$gdppc)) + geom_point() +
  geom_text(aes(label=d$iso),size=2.0, vjust=-0.35) +
  xlab("TU density (%)") + ylab("GDPpc (tys USD)") +
  scale_colour_discrete(name="") +
  geom_smooth(method=lm,se=T, size=2)

lm <- lm(data=d, gdppc ~ tu.density ); summary(lm)

Jak widać na wykresie Polska jest piąta od końca wśród krajów OECD pod względem GDP na głowę i szósta od końca jeżeli chodzi o wielkość uzwiązkowienia. Czepianie się związków w tej sytuacji (12,5% uzwiązkowienia w PL, podczas gdy przykładowo w Niemczech jest to 41.9%, a w Danii 67.2%) ma wszystkie znamiona obsesji podobnej przykładowo do popularnego wśród Palikotowców i innych antyklerykałów poglądu, iż jakoby Kościół Katolicki jest praprzyczyną wszelkiego zła (przynajmniej w PL).

Dodatkowo prosta regresja daje następujący rezultat: GDP = 0,25 tu.density + 30,5435, czyli 1% wzrost uzwiązkowienia daje 0,25 tys wzrostu GDP na głowę (dokładnie odwrotnie niż twierdzi Balcerowicz). Współczynnik przy zmiennej tu.density jest nawet istotny statystycznie (na poziomie 0,05) ale $R^2$ jest faktycznie bardzo marne -- 13%.

Zbiór danych jest do pobrania tutaj.

czwartek, 29 stycznia 2015

Faryzeuszowskie żale na Czerskiej

Głos Cadyka donosi: Austriacka gazeta wzywa Polskę, by przyznała się do udziału w Holocauście. I dalej: Czytam dzisiejsze wydanie austriackiego dziennika "Heute" i zastanawiam się, czy dziennikarstwo może upaść jeszcze niżej [...] Otóż red. Erich Nuler, który wybrał się na wtorkowe uroczystości 70. rocznicy wyzwolenia KL Auschwitz, napisał w korespondencji, że Bronisław Komorowski -- w przeciwieństwie do prezydenta i kanclerza Austrii, którzy mówili o udziale Austriaków w Holocauście -- o polskiej winie nie zająknął się nawet słowem. Jaka jest ta przemilczana polska odpowiedzialność za Holocaust?

Coż takiego oburza faryzeuszy z Czerskiej? To przecież ich postulaty. Kto jak nie GC rozpętał histerię pn. polska wina, promował czystych hochsztalerów w tym temacie typu ,,historyk'' Gross i jego beletrystyka oraz piał z zachwytu nad przeprosinami Kwaśniewskiego za zbrodnie niemieckiego Wehrmachtu (de jure -- za prawo i porządek na okupowanym obszarze odpowiada armia je zajmująca).

Inna sprawa, że faktycznie szczytem groteski jest uczenie Polaków przyzwoitości przez potomków Hitlera, Kaltenbrunnera, Globocnika czy Eberla (ten ostatni z Treblinki).

Urodzony w Wiedniu prof. Raul Hilberg (cf http://en.wikipedia.org/wiki/Raul_Hilberg) w swojej autobiografii The politics of memory: The journey of a Holocaust historian zauważa, że Austria [w roku 1992] nie jest najlepszym rynkiem dla moich książek a potem ironicznie: Najkrótsze Who's Who jakie widziałem to Austriackie wydanie z 1948 r. -- wypełnione [wyłącznie] narciarzami i śpiewakami operowymi...