Hatena::ブログ(Diary)

Tambourineの日記

2018-08-02

Rを触ってみろ

08:57

・・・と会社から言われたので、イジってみる。とりあえず、Rを使うような仕事が目の前にあるわけではないんだけども。

R自体は

https://www.r-project.org/

から入れる。今回は今時点の最新らしいR-3.5.1.pkgをダウンロードした。あとはインストーラーの言われるがまま。ダウンロードの場所にカスタムインストールすればtcltk版とかいらなさそーなものを省けるよと書いてあるけど、気にせず突っ込んだ。

R Studioも入れるが良いとのことなので

https://www.rstudio.com/

からダウンロードする。インストールは/Applicationsに.appを投げ込むだけ。大学時代からNeXTを触っているのにこのあたり、いまいちわかってない。やると何が起こるの?なんでこれだけでLaunchPadに出てくるの?

教科書は、スタンフォードのオンラインラーニング(https://lagunita.stanford.edu/courses/HumanitiesSciences/StatLearning/Winter2016/about)で使えと言われているものを使う。というか、400ページの英語の教科書とか読める気はしない。

2018-05-03

Apache Spark で遊びたい(4)

| 00:54

Quick Startの続き。"More on Dataset Operations"に沿って遊んでみる。

データは、大昔のとあるサーバのjavaheapのデータを使うことにする。こんなデータ。

> head -n5 javaheap.app01 
09:00:11	550792720	1543502336
09:01:10	321361976	1543502336
09:02:15	485179520	1543502336
09:03:10	96284072	1543502336
09:04:11	839722800	1543502336

時刻、ヒープ使用量、ヒープサイズの順。ヒープ使用量がヒープサイズに近づくとヒープ拡張が起きる。ヒープ拡張と、大量のGCが起きるタイミングがサーバのパフォーマンス的にヤバいポイントだったので、こういうデータを取っていた。もう10年前のデータで、(今はよく知らないんだけど)その当時の32bit JDKで最大ヒープサイズ4GBは結構キツかったことを覚えている。

読み込んで、ヒープ使用量の最大値を探してみよう。

まず、ファイルを読み込む。

scala> val heap01 = spark.read.textFile("javaheap.app01")
2018-05-03 22:54:45 WARN  ObjectStore:568 - Failed to get database global_temp, returning NoSuchObjectException
heap01: org.apache.spark.sql.Dataset[String] = [value: string]

次に、ヒープ使用量の列だけ取り出す。Scalaの文字列は基本的にjava.lang.Stringだけど、いろいろとトレイトは足されているらしい。まだ勉強中でよくわからない。とりあえず、String#splitはjava.lang.Stringのものが使える。それにしても、正規表現正規表現オブジェクトじゃなくて、文字列で渡すの意味わからない。そして、それを数字に変換する。

scala> heap01.map(line => line.split("\\s+")).map(ary => ary(1).toInt).first
2018-05-03 23:19:37 ERROR Executor:91 - Exception in task 0.0 in stage 2.0 (TID 2)
java.lang.NumberFormatException: For input string: "2456209624"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:583)
	at java.lang.Integer.parseInt(Integer.java:615)
(以下略)

おっと、軽く2^31を上回ってる値があった。

scala> heap01.map(line => line.split("\\s+")).map(ary => ary(1).toLong).first
res4: Long = 550792720

最大を求めてみよう。まずは、reduceで。

scala> heap01.map(line => line.split("\\s+")).map(ary => ary(1).toLong).reduce((a, b) => if (a > b) a else b)
res5: Long = 3353203544

もちろん、普通はmaxを使うだろう。

scala> heap01.map(line => line.split("\\s+")).map(ary => ary(1).toLong).max
<console>:26: error: value max is not a member of org.apache.spark.sql.Dataset[Long]
       heap01.map(line => line.split("\\s+")).map(ary => ary(1).toLong).max

え、違うの?

Quick Startに書いてあるのはこうだった。

scala> heap01.map(line => line.split("\\s+")).map(ary => ary(1).toLong).reduce((a, b) => Math.max(a, b))
res7: Long = 3353203544

あっているか自信がないので、irbで確かめる。

> irb
irb(main):001:0> heap01 = File.readlines("javaheap.app01")
=> ["09:00:11\t550792720\t1543502336\n", (以下略)
irb(main):002:0> heap01.map{|line| line.split(/\s+/).at(1).to_i}.max
=> 3353203544

整数のサイズを気にしなくて良いのは、Rubyの美点である。そういえば、最近(かな?2.4からですね)、Fixnumなくなったらしい。もちろん、内部的にCのintでおさまる整数値は特別扱いされることは変わらない。

次の例は、flatMapとgroupByKeyを使うもの。flatMapは置いておくことにして、groupByKeyで、各最大ヒープ値の列が何行あるか、数えてみることにする。

scala> heap01.map(line => line.split("\\s+")).groupByKey(ary => ary(2))
res0: org.apache.spark.sql.KeyValueGroupedDataset[String,Array[String]] = KeyValueGroupedDataset: [key: [value: string], value: [value: array<string>]]

scala> val maxHeaps = heap01.map(line => line.split("\\s+")).groupByKey(ary => ary(2)).count
maxHeaps: org.apache.spark.sql.Dataset[(String, Long)] = [value: string, count(1): bigint]

scala> maxHeaps.collect
res1: Array[(String, Long)] = Array((3162503680,4), (2558523904,1), (1543502336,19), (1816132096,2), (2462054912,14), (2822765056,65), (0,2), (4253022720,1), (3103783424,26), (2101344768,18), (2554329600,1), (4248828416,27), (4244634112,249), (2407528960,2), (2378168832,34), (2864708096,3), (2575301120,2), (3154115072,3), (2483026432,2), (2537552384,2), (2629827072,3), (4299160064,6), (3674208768,2), (2466249216,7), (3124754944,35), (2625632768,21), (2533358080,42), (2793404928,106), (3091200512,64), (2109733376,5), (1795160576,9), (2881485312,3))

groupByKeyすると、戻りの型はKeyValueGroupedDataset[String, Array[String] ] になる。これはなんとなくわからなくもない。直感的にはDataset[Taple[String, Array[String] ] ] が出来れば良いような気がする。.NETのGroupByはそんな感じだった気がする。調べた。VB風に書くと、戻りの型はIEnumerable(Of IGrouping(Of TKey, TElement))である。RubyもEnumerable#group_byの戻りはHashだ。やってみよう。

irb(main):004:0> heap01.map{|line| line.split(/\s+/)}.group_by{|ary| ary[2]}
=> {"1543502336"=>[["09:00:11", "550792720", "1543502336"], (略)

それに対してcountを実施すると、Mapのキーの数が返ってくることを期待すると思うんだ。

irb(main):005:0> heap01.map{|line| line.split(/\s+/)}.group_by{|ary| ary[2]}.count
=> 32

しかし、KeyValueGroupedDataset#countはDataset[(K, Long)]を返すことになっている。つまり、キーとそのキーでグルーピングされた要素の数のタプルのDatasetである。うーん、便利なのか。便利かな。よくわからない。

Apache Spark で遊びたい(3)

| 22:02

DatasetのAPIドキュメントは、

> https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.Dataset

にあると書いている。次に進む前にちょっと眺めてみる。

説明書きに書いてあることをまとめておく。

  • Dataset とは別にDataFrameというものもある。Datasetのuntyped viewだと書いてあるが、意味はよくわからない
  • Datasetの操作には、transformationとactionがある。Streamの中間操作と終端操作みたいなものだと思われる
  • Datasetはlazyである。まあ、そうでしょうな。actionが実行されたときに、Sparkのquery optimizerがどのように実行するか決める。RDBみたいに、explainも取れる(マジカ)。
  • domain-specific object は、Encoderを必要とする。ここでいうdomain-specific objectは、Dataset[T]型のTの部分のことで、domain-specificっていうところのドメインは、つまりは「業務上の」ぐらいの意味だと思われる。ドメインもいろんなコンテキストで使われる言葉でややこしい。Encoderは、そのTをSparkの内部表現に変換するものことらしい。その内部表現が知りたかったら、schema ってメソッドで取れる。

正直、しんどい。

メソッドの方は、Stream APIや、.NETのLINQのIEnumerable, RubyのEnumerableにあるようなものが並んでいるので安心。

目についたものをメモ

Action
describe
カラム名を渡すと、平均や最大、最小などありがちな統計情報をまとめてくれる
Basic Dataset function
cache, persist
永続化してくれる。メモリかディスクかは設定による
checkpoint
バージョン付けて一時保存して、あとで指定したバージョンに戻してくれる。
hint
今のデータセットにコメントをつける
Typed transformations
alias, as
コピーをつくる
sample
ランダムなサンプリングをする

Apache Spark で遊びたい(2)

| 19:18

本家のQuick Start(https://spark.apache.org/docs/latest/quick-start.html)をやってみる。

最近ちょっと勉強しかけているScalaの方をやってみる。

> spark-shell 
2018-05-03 16:40:22 WARN  NativeCodeLoader:62 - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Spark context Web UI available at http://192.168.0.116:4040
Spark context available as 'sc' (master = local[*], app id = local-1525333240695).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.3.0
      /_/
         
Using Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_112)
Type in expressions to have them evaluated.
Type :help for more information.

scala> 

なんかWarningがでているけど、よくわからない。

まずは、Datasetというものを作ってみろとのこと。テキストファイルからも作れるらしい。とりあえず手元にあったWASのSystemOut.logを食わせてみる。100MBぐらいのサイズ。

scala> val testFile = spark.read.textFile("SystemOut.log")
2018-05-03 17:30:36 WARN  ObjectStore:6666 - Version information not found in metastore. hive.metastore.schema.verification is not enabled so recording the schema version 1.2.0
2018-05-03 17:30:36 WARN  ObjectStore:568 - Failed to get database default, returning NoSuchObjectException
2018-05-03 17:30:38 WARN  ObjectStore:568 - Failed to get database global_temp, returning NoSuchObjectException
testFile: org.apache.spark.sql.Dataset[String] = [value: string]

相変わらずWarningは出るものの、ちゃんとできたっぽい。

scala> testFile.count
res4: Long = 160086                                                             

scala> testFile.first
res5: String = ************ Start Display Current Environment ************

件数をとったり、1レコード目を取ったり。

> wc -l SystemOut.log
  160086 SystemOut.log                                            
> head -n1 SystemOut.log 
************ Start Display Current Environment ************

うん。あってる(あたりまえ)。

filterを使って、新しいDatasetを作ることもできる

scala> val errLine = testFile.filter(line => line.contains("ERROR"))
errLine: org.apache.spark.sql.Dataset[String] = [value: string]

scala> errLine.count
res6: Long = 166

chainさせて1行でやってもいい

scala> testFile.filter(line => line.contains("ERROR")).count
res7: Long = 166

まあ、こういうことだな。

> grep ERROR SystemOut.log |wc -l
     166

うむ、あってる(あたりまえだってば)。

Apache Spark で遊びたい(1)

| 11:54

なんだかインストールばっかりしている気がするが、まあ、そんなものである。

社内の勉強会で、ちょっくらApache Sparkを触ってみることになった。

まずは、環境の確認

> python --version
Python 3.6.0 :: Anaconda 4.3.0 (x86_64)                                             
> java -version
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b16, mixed mode)                                        
> scala -version
Scala code runner version 2.12.5 -- Copyright 2002-2018, LAMP/EPFL and Lightbend, Inc.

インストールは例によって、homebrew

> brew install spark
==> Downloading https://github.com/holman/spark/archive/v1.0.1.tar.gz
==> Downloading from https://codeload.github.com/holman/spark/tar.gz/v1.0.1
######################################################################## 100.0%
&#127866;  /usr/local/Cellar/spark/1.0.1: 6 files, 6.9KB, built in 7 seconds

・・・あれ?なんか違うものがはいった。これじゃない。

> brew search spark
==> Searching local taps...
apache-spark               spark                      sparkey
==> Searching taps on GitHub...
caskroom/cask/cisco-spark                caskroom/cask/spark-inspector
caskroom/cask/spark
==> Searching blacklisted, migrated and deleted formulae...

なるほど。

> brew install apache-spark
==> Downloading https://www.apache.org/dyn/closer.lua?path=spark/spark-2.3.0/spa
==> Best Mirror http://ftp.jaist.ac.jp/pub/apache/spark/spark-2.3.0/spark-2.3.0-
######################################################################## 100.0%
&#127866;  /usr/local/Cellar/apache-spark/2.3.0: 1,018 files, 243.7MB, built in 1 minute

2017-10-05

Hyperledgerで遊ぶ(1) - nvmのインストールで挫折

14:31

何やらインストールばかりしているようで何も身についていない気がしなくもないが、今度はHyperledgerで遊んでみることになった。

https://hyperledger.github.io/composer/installing/installing-index.html

を見ながら進めてみることにする。

"Installing the pre-requisites for Hyperledger Composer on Mac OS X"という節には、まずnvmを入れろと書いてあるので、homebrewインストールした。

インストールは成功し、インストール時のメッセージで、.bashrcで/usr/local/opt/nvm/nvm.shを実行するようにと言われた。私は今はfishを使っているので、.config/fish/config.fishで実行するようにしたところ

Unsupported use of '||'. In fish, please use 'COMMAND; or COMMAND'.
/usr/local/opt/nvm/nvm.sh (line 16):   command printf %s\\n "$*" 2>/dev/null || {

と怒られた。/usr/local/opt/nvm/README.mdによると

**Note:** `nvm` does not support [Fish] either (see [#303](https://github.com/creationix/nvm/issues/303)). 

だそうだ。

うーむ・・・と唸っていると、周りのメンバーは誰もちゃんとnvmを入れずに進んでいることがわかったので、とりあえずこのまま進める(笑)。ダメだったら、bashでやり直そう。

Hyperledgerで遊ぶ(2) - いろいろとインストール

14:54

引き続き、pre-reqをインストールしていく。nodeはインストール済みなので、brew upgrade nodeで最新化。Dockerbrew cask install dockerする。

VSCodeMicrosoftからダウンロードして普通にインストールした。Hyperledger Composer Extension for VSCodeは、起動したVSCode拡張機能からインストールする。ついでに、Vimキーバインドエミュレーションも入れておかないと仕事にならないので、いれる。

Hyperledgerで遊ぶ(3) - node.jsはv6じゃないとだめだって!

20:40

で、いよいよHyperledgerを入れようとすると、node.jsのバージョンはv6までじゃないとだめだという

https://hyperledger.github.io/composer/installing/development-tools.html

The following are prerequisites for installing the required development tools:

    Node: 6.x (note versions 7 and higher are not supported)

マジカヨ。それでnvmなのね。

みんなどうしてんの?とチームメンバーに聞いたら、nodebrewを使っているというので、それを使ってみる。インストール方法は、

https://qiita.com/akakuro43/items/600e7e4695588ab2958d

に書いてあるけど、nodebrew install-binary lastestする前に、~/.nodebrew/srcを手で作っておかないと失敗する罠があった。

とりあえず、これでいいのかな?

Hyperledgerで遊ぶ(4) - いよいよインストール

21:11

npmでインストールするらしい。

> npm install -g composer-cli
npm WARN deprecated fs-promise@1.0.0: Use mz or fs-extra^3.0 with Promise Support
npm WARN deprecated crypto@0.0.3: This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.
npm WARN deprecated jade@1.11.0: Jade has been renamed to pug, please install the latest version of pug instead of jade
npm WARN deprecated transformers@2.1.0: Deprecated, use jstransformer
npm WARN deprecated nodemailer@2.7.2: All versions below 4.0.1 of Nodemailer are deprecated. See https://nodemailer.com/status/
/Users/tambara/.nodebrew/node/v6.11.4/bin/composer -> /Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/composer-cli/cli.js

> fsevents@1.1.2 install /Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/composer-cli/node_modules/fsevents
> node install

[fsevents] Success: "/Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/composer-cli/node_modules/fsevents/lib/binding/Release/node-v48-darwin-x64/fse.node" already installed
Pass --update-binary to reinstall or --build-from-source to recompile

> pkcs11js@1.0.9 install /Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/composer-cli/node_modules/pkcs11js
> npm run build


> pkcs11js@1.0.9 build /Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/composer-cli/node_modules/pkcs11js
> node-gyp configure build

gyp ERR! configure error 
gyp ERR! stack Error: Python executable "/Users/tambara/.pyenv/shims/python" is v3.6.0, which is not supported by gyp.
gyp ERR! stack You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.
gyp ERR! stack     at failPythonVersion (/Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:454:14)
gyp ERR! stack     at /Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:443:9
gyp ERR! stack     at ChildProcess.exithandler (child_process.js:189:7)
gyp ERR! stack     at emitTwo (events.js:106:13)
gyp ERR! stack     at ChildProcess.emit (events.js:191:7)
gyp ERR! stack     at maybeClose (internal/child_process.js:920:16)
gyp ERR! stack     at Socket.<anonymous> (internal/child_process.js:351:11)
gyp ERR! stack     at emitOne (events.js:96:13)
gyp ERR! stack     at Socket.emit (events.js:188:7)
gyp ERR! stack     at Pipe._handle.close [as _onclose] (net.js:497:12)
gyp ERR! System Darwin 16.7.0
gyp ERR! command "/Users/tambara/.nodebrew/node/v6.11.4/bin/node" "/Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "configure" "build"
gyp ERR! cwd /Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/composer-cli/node_modules/pkcs11js
gyp ERR! node -v v6.11.4
gyp ERR! node-gyp -v v3.4.0
gyp ERR! not ok 

npm ERR! Darwin 16.7.0
npm ERR! argv "/Users/tambara/.nodebrew/node/v6.11.4/bin/node" "/Users/tambara/.nodebrew/node/v6.11.4/bin/npm" "run" "build"
npm ERR! node v6.11.4
npm ERR! npm  v3.10.10
npm ERR! code ELIFECYCLE
npm ERR! pkcs11js@1.0.9 build: `node-gyp configure build`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the pkcs11js@1.0.9 build script 'node-gyp configure build'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the pkcs11js package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node-gyp configure build
npm ERR! You can get information on how to open an issue for this project with:
npm ERR!     npm bugs pkcs11js
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls pkcs11js
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/tambara/.nodebrew/node/v6.11.4/lib/node_modules/composer-cli/node_modules/pkcs11js/npm-debug.log
/Users/tambara/.nodebrew/node/v6.11.4/lib
└── (empty)

npm ERR! Darwin 16.7.0
npm ERR! argv "/Users/tambara/.nodebrew/node/v6.11.4/bin/node" "/Users/tambara/.nodebrew/current/bin/npm" "install" "-g" "composer-cli"
npm ERR! node v6.11.4
npm ERR! npm  v3.10.10
npm ERR! code ELIFECYCLE

npm ERR! pkcs11js@1.0.9 install: `npm run build`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the pkcs11js@1.0.9 install script 'npm run build'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the pkcs11js package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     npm run build
npm ERR! You can get information on how to open an issue for this project with:
npm ERR!     npm bugs pkcs11js
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls pkcs11js
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/tambara/npm-debug.log
npm ERR! code 1

あ゛あ゛あ゛、手元の蛇がPython3になっていた・・・

2017-10-03

Gradleでjarをつくる(4)

11:05

CommonとCommonDAOの間の依存関係はちゃんと作れた。

後は、2つのプロジェクトのbuild.gradleの共通部分を、architect直下のbuild.gradleに移しておく。

  • architect/
    • setting.gradle
    • build.gradle
    • Common/
      • build.gradle
    • CommonDAO/
      • build.gradle

共通のbuild.gradleには

subprojects {
  apply plugin: 'java'

  sourceSets {
    main {
      java {
        srcDir 'src'
      }
    }
  }
}

を記述し、各プロジェクト下のbuild.gradleはdependenciesだけになった

2017-09-25

Gradleでjarをつくる(3)

19:19

こんどは(2)でビルドしたプロジェクトに依存しているプロジェクトをビルドする。ちなみに今回ビルドしようとしているのは、Commonプロジェクト。前回ビルドしたのはCommonDAOプロジェクトである。

もちろん、順番にビルドすることにして、依存先のbuildディレクトリ以下のjarファイルにdependenciesを記述してもできると思うが、それではmake以下である。

プロジェクトに依存する場合には、このように書くらしい。

apply plugin: 'java'

sourceSets {
  main {
    java {
      srcDir 'src'
    }
  }
}

dependencies {
  compile project(':CommonDAO')
}

もちろんうまくいかない。

> gradle assemble

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/tambara/hoge/architect/Common/build.gradle' line: 12

* What went wrong:
A problem occurred evaluating root project 'Common'.
> Project with path ':CommonDAO' could not be found in root project 'Common'.

CommonDAOなんて見つからないよと言っている。Gradleはパスの上下関係でプロジェクトの上下関係を認識するようなので、CommonとCommonDAOが横に並んでいるからダメなのだ。

プロジェクト構成は、settings.gradleというファイルで指定する。プロジェクト構成を知るために、Gradleはパスを遡ってsettings.gradleを探す。なので、今回の場合は、CommonとCommonDAOと同じレベルに置いてやれば良い

  • architect/
    • settings.gradle
    • Common/
    • CommonDAO/

こんな感じ。中身は

include 'Common'
include 'CommonDAO'

だけが書いてある。これで、Commonの下でgradle assembleすると、まずCommonDAOのビルドがスタートする。ばっちりである。