だれかのメモ帖

テクノロジー全般のメモ

R勉強メモ(基礎編)

ISITのRを用いたデータ分析基礎を受講したのでメモ

Rとは?

Rの概要

Rの基礎

Rを用いたデータ分析の基礎

Rのオブジェクト

変数、データ、関数など 名前を付ける=付値

[オブジェクト名] <- [値] でオブジェクトに値を代入する。

> X <- 1

Rの作業ディレクトリ

以下で確認可能 作業ディレクトリ配下のデータファイルを読み込む

> getwd()
[1] "/hogehoge"

作業ディレクトリを変更するとき

> setwd("/work")

helpと終了

> help(関数名)
> q()
> quit()

データタイプとデータ構造

型+構造でデータを表現

 データタイプ

  • logical 2値
  • integer 整数
  • double 実数
  • complex 複素数
  • character 文字列(演算はできない)

データタイプの確認方法

> typeof(obj)

Rはデータタイプを自動的に決定する。

数値は明示しない限りdouble型 タイプが混じった演算はより大きい型になる

データ構造

  • スカラ
  • ベクタ
  • 行列
  • リスト
  • データフレーム (ベクタをまとめて表とする)

スカラ

単一のデータを持つ 定数、変数

ベクタ

同一データタイプを一次元で列挙したもの

要素アクセス ベクタ[要素番号] ベクタ[c(要素番号, 要素番号...)]

数値ベクタ

> 3:6
[1] 3 4 5 6

seq関数を使って、初項、末項、公差を指定してベクタを生成する。

> seq(4, 8, by=2)
[1] 4 6 8 

rep関数を使って、xをm回繰り返して並べたベクタを生成

> rep(6:7, times=2)
[1] 6 7 6 7 

c関数を使って、任意のベクタを生成

> c(1,2,1,1)
v1 <- 1:5

ベクタ同士を連結

> c(v1, 6:10)
1 2 3 4 ... 10

scan関数を使って、ファイルからベクタを読み込み

scan("ファイル名", skip=s, nlines=n) skipは先頭s行をスキップすることを示している。 nlinesはn行のデータを読み込むことを示している。

rnorm(n, mean=m, sd=d)を使ってガウス分布に従った、乱数列を得る 項数n, 平均m, 標準偏差d

runif(n, max=mx, min=mn)を使って一様分布に従った、乱数列を得る 項数n, 最大値max, 最小値min

その他もあり

行列

列と行に要素を並べたデータ型

matrix(v1, nrow=n1, ncol=n2, byrow={TRUE|FALSE}) ベクタv1からn1行n2列の行列を生成 byrowで列優先か行優先かきまる。(元のベクタが列に準ずるか行に順ずるか)

n1n2がv1の要素数と一致 =>  v1の全要素が行列化 n1n2がv1の要素数より大きい => v1の要素を繰り返し n1n2がv1の要素数より小さい => v1の要素をn1n2までの要素で生成

元のベクタのもっとも表現力の高いデータタイプになる ncolを省略した場合は自動で決定。(割り切れない場合は上のルールでデータ埋められる) byrowはデフォルトTRUE

要素取り出し

mat[3] => 3行目1列のデータをスカラ値として返す mat[1,1] => 1行1列のデータをスカラ値として返す mat[1,] => 1行目のすべてをベクタとして返す mat[,1] => 1列目のすべてをベクタとして返す mat[c(1,3),] => 1,3行目を抜き出した新しい行列を返す

リスト

異なるタイプのオブジェクトを一次元に並べて管理するデータ型

list(n1=o1, n2=o2,...) キーn1にオブジェクトo1を格納 キーn2にオブジェクト02を格納 したリストを生成する。

> testList <- list(name="test", number=20)
> testList
$name
[1] "test"

$number
[1] 20

str(<listオブジェクト>) でリストの構造を表示する

> str(testList)
List of 2
 $ name  : chr "test"
 $ number: num 20

<リストオブジェクト>$<キーの名前> <リストオブジェクト>2 <リストオブジェクト>"<キーの名前>" で要素アクセスする

また、 <リストオブジェクト>[数値範囲] <リストオブジェクト>["<キーの名前>"] とすると リスト型で返却される

> testList$number
[1] 20

データフレーム

リストの一種 表を表現したもの

1要素 (=行) は異なるタイプのオブジェクトの列となっている 1変量 (=列) はデータフレーム中ですべて同じ要素数となっている必要がある。

data.frame()によってデータフレームを生成する。

read.csv("<ファイル名>", header={TURE(default)|FALSE}, sep=","(default)) によって、CSVファイルを読出してデータフレームに付値する

ベクタの羅列からデータフレームを生成することも可能 ※各ベクタの要素数は同じである必要がある

str(<データフレームオブジェクト>)で構造を確認可能

> df<-data.frame()
> df<-read.csv("example-readtable.csv")
> df
  Name Japanese Math English Chemistry Geography
1 AAAA       55   70      80        45        60
2 BBBB       90   50      90        40        80
3 CCCC       60   65      65        60        60
4 DDDD       80   85      85        80        70
5 EEEE       95   40     100        70        50
> str(df)
'data.frame':   5 obs. of  6 variables:
 $ Name     : Factor w/ 5 levels "AAAA","BBBB",..: 1 2 3 4 5
 $ Japanese : int  55 90 60 80 95
 $ Math     : int  70 50 65 85 40
 $ English  : int  80 90 65 85 100
 $ Chemistry: int  45 40 60 80 70
 $ Geography: int  60 80 60 70 50

要素アクセス

一重の大かっこ <データフレームオブジェクト>[1] <データフレームオブジェクト>["<変量名>"]

=> ある一変量の全データがデータフレーム型で返却される

二重の大かっこ [] <データフレームオブジェクト>1 <データフレームオブジェクト>"<変量名>"

=> ある一変量の全データがベクタ型で返却される

> df[2]
  Japanese
1       55
2       90
3       60
4       80
5       95
> df[[2]]
[1] 55 90 60 80 95

<データフレームオブジェクト>[<行番号>, <列番号|変量名>]

=> ある一要素、または部分データフレーム

※ある行すべてを抜き出す場合列番号は省略

> df[1,1]
[1] AAAA
Levels: AAAA BBBB CCCC DDDD EEEE
> df[1,1:2]
  Name Japanese
1 AAAA       55

データフレームからのデータ抽出

subset(<データフレーム>, subset=抽出条件, select=抽出変量)関数で、条件に従ったデータを抜き出し

> subset(df, subset=Japanese>70)
  Name Japanese Math English Chemistry Geography
2 BBBB       90   50      90        40        80
4 DDDD       80   85      85        80        70
5 EEEE       95   40     100        70        50

> subset(df, subset=Japanese>70, select = c(Japanese,English))
  Japanese English
2       90      90
4       80      85
5       95     100

データフレームの結合

merge(x, y) x,y データフレームの結合 共通の変量で同じ要素を持つものはマージされる

edit(df) df データフレーム エディタが表示され、データを編集できる ※返り値に編集されたデータフレームが返却される。

因子(Factor)

文字列などは因子型として管理される 文字列ベクタに現れる要素に水準値をつけ、その水準値でデータを表現する。

factor(v) vはベクタ

水準は辞書順に値が与えられる。

水準値を自分で与えることもできる。

factor(v, levels=lv, ordered={TRUE|FALSE}) lv 水準ベクタ ※lvに含まれていない、要素がvにあるとエラー

ordered 順序関係を規定するか?(TRUEの場合、水準ベクタの要素順に大小関係が規定されている) ※四則演算には意味がないのでエラー

演算

四則演算

スカラとベクタの四則演算

ベクタのすべての要素にスカラ値との四則演算結果となる

ベクタベクタの四則演算

対応する要素同士の演算となる

ベクタベクタの比較演算

論理値ベクタが返る

素数が異なるベクタ同士の演算

長い方に合わせて、短い方を繰り返す

ベクタの演算

内積外積

行列の演算

四則演算 各要素の演算 行列演算も可能

データの性質

質的データ

四則演算ができないデータ

名義尺度

便宜的に、数値を割り振ったデータ

一致/不一致 比較、計数

順序尺度

順序に意味がる名義尺度

大小比較

量的データ

間隔尺度

順序が等間隔で定義される順序尺度

加減算が可能

比例尺度

原点が設定されている間隔尺度

剰余算が可能

DBGENを使って、PostgreSQLにTPC-H測定用データを格納

DBGEM導入

1. DBGENのダウンロード、展開

TPC-Hのサイトからダウンロードします。

$ wget http://www.tpc.org/tpch/spec/tpch_2_17_0.zip
$ unzip tpch_2_17_0.zip

2. DBGENのインストール

makefile.suiteを環境に合わせて書き換えます。

$ cd tpch_2_17_0/dbgen
$ vi makefile.suite

今回は、

で試しています。

以下の部分だけ変更しました。

CC      = gcc
DATABASE= ORACLE
MACHINE = LINUX
WORKLOAD = TPCH
LDFLAGS = -O3

そして、コンパイル

$ make -f makefile.suite

dbgenとqgenという実行ファイルが生成されます。

3. テストデータ生成

$ ./dbgen -s 10

-s オプションで生成されるデータのスケールを決めます。 上記で10GBのテストデータが生成されます。 以下のファイルが生成されていれば成功だと思います。

  • customer.tbl
  • lineitem.tbl
  • nation.tbl
  • orders.tbl
  • part.tbl
  • partsupp.tbl
  • region.tbl
  • supplier.tbl

PostgreSQLにテストデータをロード

データベース作成とかは省略。 testdbというデータベースを作成したものとします。

1. テーブルを作成

dbgen ディレクトリの配下に、dll文が用意されているのでそれを利用します。

$ psql -d testdb -f dss.ddl

これは特に変更しなくても使えました。

2. データをロード

COPY文を使って"*.tbl"をロードします。 が、そのままでは失敗します。。。

DBGENは、テストデータの行末にデリミタを入れるのですが

1|AMERICA|hs use ironic, even requests. s|

PostgreSQLは最後のデリミタの右側にデータがあると怒ります。。。

testdb=# copy region from '/home/postgres/tpch_data/region.tbl' with delimiter '|';
ERROR:  extra data after last expected column
CONTEXT:  COPY region, line 1: "0|AFRICA|lar deposits. blithely final packages cajole. regular waters are final requests. regular ac..."

この解決方法ですが色々調べた結果、 sedコマンドからのパイプラインとCOPY文の標準入力からのロード機能を使うのがスマートで良さそうです。

$ sed -e 's/|$//' supplier.tbl | psql -d testdb -c "copy supplier from STDIN ( delimiter '|');"

ちなみにデフォルトの設定だと、チェックポイントの間隔が身近すぎるとWARNINGを出されるので設定を変えておくと良いかもしれません。

3. 制約の追加

dss.riを使ってインデックスを追加します。 ただし、これも修正が必要です。

修正点は以下

  • 文の削除
    • -- CONNECT TO TPCD;
    • -- COMMIT WORK;
  • 各テーブル名からTPCDスキーマの削除
  • 外部キーの追加文の修正

外部キーの追加文は

ALTER TABLE NATION
ADD FOREIGN KEY NATION_FK1 (N_REGIONKEY) references REGION;

となっているものを

ALTER TABLE NATION
ADD FOREIGN KEY (N_REGIONKEY) references REGION(R_REGIONKEY);

と変えました。 そして実行。

$ psql -d testdb -f dss.ri

データのロード、インデックスの追加はかなり時間がかかりますのでコーヒーでも飲みながら気長に待ちましょう。

Chefのまとめ

chefの勉強したので記録。
とりあえず、chefはここまでにして次は違うことをやるつもり。

1. 準備

$ knife solo init chef-repo
$ cd chef-repo
$ knife solo prepare [host名]
※[host名]はsshで接続できる名前
$ knife cookbook create [name] -o site-cookbooks/

そうするとこんな感じでディレクトリ・ファイルができる

chef-repo/
----- site-cookbooks/
------ [name]/
------ recipes/
------ default.rb =>編集対象、nodeで実行する内容を書く
------ template/ ノードに送信するファイルを格納する。
------ nodes/
| |------ [host名].json =>レシピの実行などをjson形式で記述

2. ノードで任意の処理を実行

やること
・default.rbの編集
・[host名].jsonの編集

その後、以下を実行。

$ knife solo cook [host名]

するとdefault.rbに書かれた内容が実行される。

何ができるの?
defaut.rbに書けるノードで実行するさまざまな命令を総称してResourcesという。

resourcesの例(一部)
package
さまざまなパッケージをインストールする

service
OSサービスをコントロールする

template
ファイルを操作する。erbファイルをtemplateフォルダに置くことで、その内容がノードに書かれる。
値を変数として埋め込むことができる。

gem_package
gemパッケージをインストールする

cookbook_file
ファイルを転送する


あとは、http://docs.opscode.com/chef/resources.html
を見て勉強すべし。

SSHの設定

Vagrantでは、仮想マシンsshで乗り込むために

$ vagrant ssh

というサブコマンドが用意されている。

でも、普通にsshコマンドでも接続したい場合もある。
その場合、ssh接続設定をvagrantサブコマンドから取得できる。
取得した情報は~/.ssh/configに書けばいい。それを一気にやるには以下のようにコマンドを打つ。

$ vagrant ssh-config --host [ホスト名] >> ~/.ssh/config

ただしこのとき、OpenSSHの最新バージョンを使っていると以下のように怒られるかもしれない。

$ ssh [ホスト名]
Bad owner or permissions on /home/[OSユーザー名]/.ssh/config

これは、.ssh/configファイルのパーミッションの問題。
OpenSSHの最新版ではセキュリティが強化されているため、設定ファイルが他のユーザから見えないようにしていないと接続ができない。
なので、以下のようにパーミッションを変えて解決

$ sudo chmod 600 ~/.ssh/config

プロビジョニングとBox作成

Vagrant仮想マシンを起動するときに、任意の操作を実行する。

Vagrantfileに以下を追加する。

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  config.vm.box = "centos32"
  config.vm.provision :shell,:inline => "echo hello world" #この行

end

ダブルクォーテーションでくくったシェルコマンドが実行される。

シェルのファイルを指定するには、

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  config.vm.box = "centos32"
  config.vm.provision :shell,:path => "provisioning.sh" #この行

end

とする。

オリジナルBoxの作成

VagrantのBox作成コマンドを使って、現在の仮想マシンをの状態からBoxを作成することができる。
スナップショットが丸々複製されるようなものだと理解している。

作り方は非常に簡単だ。
現在の状態をBoxにしたい仮想マシンvagrant initしたディレクトリに移動して以下を実行するだけ。

$ vagrant package
$ vagrant box add [好きなBox名] package.box

以上。
box addしたら、package.boxは不要になるので消去しても良い。

おもむろにVagrantを使おうとしたが躓いた

  • 準備
$ vagrant box add mybox [link]

で、ボックスを取得。myboxには好きな名前を付ける。
ボックスは
http://www.vagrantbox.es/
から持ってくる。

$ vagrant box list

で取得済みのリストを確認


$ mkdir myVM
$ cd myVM
$ vagrant init mybox 
$ vagrant up

…がだめ。
以下のエラーが出た。

VirtualBox is complaining that the kernel module is not loaded. Please
run `VBoxManage --version` or open the VirtualBox GUI to see the error
message which should contain instructions on how to fix this error.

言われた通りにrunしてみる。

$ VBoxManage --version
WARNING: The vboxdrv kernel module is not loaded. Either there is no module
         available for the current kernel (2.6.32-431.el6.i686) or it failed to
         load. Please recompile the kernel module and install it by

           sudo /etc/init.d/vboxdrv setup

         You will not be able to start VMs until this problem is fixed.
4.3.8r92456

やっぱり、素直にやってみる

# /etc/init.d/vboxdrv setup
Stopping VirtualBox kernel modules                         [  OK  ]
Uninstalling old VirtualBox DKMS kernel modules            [  OK  ]
Trying to register the VirtualBox kernel modules using DKMS
Error! Your kernel headers for kernel 2.6.32-431.el6.i686 cannot be found at
/lib/modules/2.6.32-431.el6.i686/build or /lib/modules/2.6.32-431.el6.i686/source.
                                                           [失敗]
  (Failed, trying without DKMS)
Recompiling VirtualBox kernel modules                      [失敗]
  (Look at /var/log/vbox-install.log to find out what went wrong)

失敗しやがった。

/lib/modules/2.6.32-431.el6.i686/
を見に行くと、なんとbuildのリンク先がおかしかったので修正。

build -> ../../../usr/src/kernels/2.6.32-431.el6.i686

から

build -> ../../../usr/src/kernels/2.6.32-431.5.1.el6.i686

やり直してみる。

# /etc/init.d/vboxdrv setup
Stopping VirtualBox kernel modules                         [  OK  ]
Uninstalling old VirtualBox DKMS kernel modules            [  OK  ]
Trying to register the VirtualBox kernel modules using DKMS[  OK  ]
Starting VirtualBox kernel modules                         [  OK  ]

成功した。
このあと、 vagrant up したら仮想マシンの生成は成功するようになった。
でも、sshで接続しにいくとタイムアウトする。。。。これは、Vagrantfileの問題なようなので、とりあえずvagrant使える環境は整った。

Zaimのリクエストトークン取得ができない話

Javaの勉強のために「とりあえず、なにか作ろう」と思い、ZaimのAPIを叩いたアプリケーションを作ろうとしています。

が、最初の第一歩であるoauth認証で積んだのでメモ。
勉強も兼ねて、一から自分でやったる、と思って色々なところを参考にヘボクラスを作りました。

だが、Zaimのリクエストトークン用のURLにアクセスしても400を吐かれます。。。
しかし、Twitterで実験してみると、こちらでは上手くいきます。

ついでに、rubygemのoauthライブラリで実験したみたところ。やはりTwitterはリクエストトークンが返ってくるのに、Zaimでは400です。。。

というわけで、原因を調査中です。

public class OauthImpl {
	private URL url;
	private URLConnection ucon;
	
	private String consumerkey = "";
	private String consumerSecret = "";
	private String oauth_nonce = "";
	private String oauthToken = ""; 
	private String oauthTokenSecret = "";
	private String oauthSignature="";
	private String oauth_timestamp = "";

	private SortedMap<String, String> params;
	public OauthImpl(String url,String consumerkey,String consumerSecret) {
		try {
			this.url = new URL(url);

			this.consumerkey = consumerkey;
			this.consumerSecret = consumerSecret;
			
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	public void httpGet() {
		HttpURLConnection huc = (HttpURLConnection) this.ucon;
		try {
			huc.setRequestMethod("GET");
			huc.connect();
			BufferedInputStream bis = new BufferedInputStream(
					huc.getInputStream());
			int data;
			while ((data = bis.read()) != -1)
				System.out.write(data);

		} catch (ProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	public void sendRequestPost() throws Exception {
		this.oauth_timestamp = String.valueOf(new Long(System
				.currentTimeMillis() / 1000L));
		double ran = Math.random();
		this.oauth_nonce = Double.toString(ran);
		params = new TreeMap<String, String>();
		params.put("oauth_consumer_key", consumerkey);
		params.put("oauth_signature_method", "HMAC-SHA1");
		params.put("oauth_timestamp", this.oauth_timestamp);
		params.put("oauth_nonce", this.oauth_nonce);
		params.put("oauth_version", "1.0");
		params.put("oauth_callback", "oob");
		{
		//signature is constructed
			String paramStr = "";
			// 昇順で並べる
			for (Entry<String, String> param : params.entrySet()) {
				paramStr += "&" + param.getKey() + "=" + param.getValue();
			}
			// first "&" should be removed
			paramStr = paramStr.substring(1);
	
			String text;
			String signature = null;
			text = urlEncode("POST") + "&" + urlEncode(this.url.toString()) + "&"
					+ urlEncode(paramStr);
			// System.out.println("text = \n"+text);
	
			String key = urlEncode(this.consumerSecret) + "&";
	
			SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
			Mac mac = Mac.getInstance(signingKey.getAlgorithm());
			mac.init(signingKey);
			byte[] rawHmac = mac.doFinal(text.getBytes());
			signature = BASE64Encoder.encode(rawHmac);
			this.oauthSignature = signature;
			params.put("oauth_signature", signature);
		}
		String paramStr = "";
		for (Entry<String, String> param : params.entrySet()) {
				paramStr += "," + param.getKey() + "=\""
						+ urlEncode(param.getValue()) + "\"";
	
		}
		paramStr = paramStr.substring(1);
		String authorizationHeader = "OAuth " + paramStr;
		
		HttpURLConnection huc = (HttpURLConnection)  this.url.openConnection();
		huc.setRequestMethod("POST");
		// huc.setDoOutput(true);
		// System.out.println(this.createAuthHeader());
		huc.setRequestProperty("Authorization", authorizationHeader);
		huc.connect();
	
		BufferedReader reader = new BufferedReader(new InputStreamReader(
				huc.getInputStream()));
		String response;
		while ((response = reader.readLine()) != null) {
			System.out.println(response);
		}

	}


	private String urlEncode(String str) {
		try {
			return URLEncoder.encode(str, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}
}