Learn to Live and Live to Learn

IT(たまにビジネス)に関する記事を読んで、考えて、使ってみたことをまとめる場。

D3.jsでヒートマップっぽいものを作ってみた

概要

地域データを扱うことが多いので,可視化の方法としてD3.jsを勉強しました. (可視化は手段なんで,RでもTablueauでも状況に合わせて使い分ければいいと思います)

D3.js(Data Driven Documents)は

ウェブブラウザで動的コンテンツを描画するJavaScriptライブラリ(Wikipediaより)

です.

dotinstallをざっと見とくと理解が早いと思います.

今回は以下の記事をベースに地図に「都道府県別 100万人あたりの常設映画館数」をプロットしてみました. qiita.com

完成図はこちら. f:id:A_01:20170219212803p:plain 情報源:政府統計の総合窓口 GL01010101 (東京が一番かと思いきや福岡が多いんですね.スクリーンの数とか,映画館の来場回数とか,DVDやテレビなど配給方法の割合とか色々気になりますが,今回は関係ないので触れません)

準備

ツール

# ShapeかGeoJSONへの変換に利用する
$ brew install gdal

# GeoJSONからTopoJSONへの変換に利用する
$ npm install -g topojson

# 最新版のV2系では,Qiitaで使用されているtopojsonというコマンドが4つのコマンドに分割されている.
# V2ではgeo2topoが該当するが,今回はV1を指定してインストールする
$ npm install -g topojson@1.6.27

# kokoじゃなくてもいいんだけど
# 今回,外部ファイルからデータを読み込むんで,直接htmlファイルを開くだけだと外部ファイルを読み込めなず
# サーバにファイルを置いてアクセスする必要がある.kokoはそのための手段.
$ brew install node
$ npm install -g koko

データ

Natural Earthから都道府県レベルの地図データをダウンロードします.Shapeファイルなんで,GeoJSON -> TopoJSONと変換して使います.

http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip

# ne_10m_admin_1_states_provinces.shpというShapeファイルが入っているので,pref.jsonというGeoJSONに変換する
$ ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" pref.json ne_10m_admin_1_states_provinces.shp

# GeoJSONからjapan.jsonというTopoJSONに変換する
$ topojson -p name -p name_local -p latitude -p longitude -o japan.json pref.json

実装

こんな感じです. ちなみに2017/02/19時点でD3.jsの最新版はv4なんですが,アニメーションを設定しようとするとエラーになったのでhttps://d3js.org/d3.v3.min.jsを指定しています.

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utr-8">
        <title></title>
       <style type="text/css">
        .pref {
            fill: #fff;
            stroke: #aaa;
        }
        </style>
    </head>
    <body>
        <script src="https://d3js.org/d3.v3.min.js"></script>
        <script src="http://d3js.org/topojson.v0.min.js"></script>
        <script>
            var w = 1300;
            var h = 960;

            var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);

            // 地図の投影図法を設定する.
            var projection = d3.geo.mercator()
                .center([136, 35.5])
                .scale(2000)
                .translate([w / 2, h / 2]);

            // GeoJSONからpath要素を作る.
            var path = d3.geo.path()
                .projection(projection);

            var pref = {
                    "1":"北海道",
                    "2":"青森県",
                    "3":"岩手県",
                    "4":"宮城県",
                    "5":"秋田県",
                    "6":"山形県",
                    "7":"福島県",
                    "8":"茨城県",
                    "9":"栃木県",
                    "10":"群馬県",
                    "11":"埼玉県",
                    "12":"千葉県",
                    "13":"東京都",
                    "14":"神奈川県",
                    "15":"新潟県",
                    "16":"富山県",
                    "17":"石川県",
                    "18":"福井県",
                    "19":"山梨県",
                    "20":"長野県",
                    "21":"岐阜県",
                    "22":"静岡県",
                    "23":"愛知県",
                    "24":"三重県",
                    "25":"滋賀県",
                    "26":"京都府",
                    "27":"大阪府",
                    "28":"兵庫県",
                    "29":"奈良県",
                    "30":"和歌山県",
                    "31":"鳥取県",
                    "32":"島根県",
                    "33":"岡山県",
                    "34":"広島県",
                    "35":"山口県",
                    "36":"徳島県",
                    "37":"香川県",
                    "38":"愛媛県",
                    "39":"高知県",
                    "40":"福岡県",
                    "41":"佐賀県",
                    "42":"長崎県",
                    "43":"熊本県",
                    "44":"大分県",
                    "45":"宮崎県",
                    "46":"鹿児島県",
                    "47":"沖縄県"
                    };

            // TopoJSONを読み込む.
            d3.json("japan.json", function(error, japan) {
                console.log(japan);
                var topo = topojson.object(japan, japan.objects.pref).geometries;
                svg.selectAll(".pref")
                    .data(topo)
                    .enter()
                    .append("path")
                    // キーとバリューを逆にしてしまったので回避策.
                    .attr("class", function(d) {
                        var id =  Object.keys(pref).filter((key) => {
                            return pref[key] === d.properties.name_local
                            });
                        return "pref pref" + id;
                    })
                    .attr("d", path);
            });    
    
            var category = [{"name":"常設映画館数(人口100万人当たり)(館)",
                "data":{"1":10.4,
                    "2":13.6,
                    "3":14.0,
                    "4":6.0,
                    "5":13.5,
                    "6":9.7,
                    "7":7.2,
                    "8":10.3,
                    "9":8.1,
                    "10":10.6,
                    "11":4.1,
                    "12":5.6,
                    "13":21.4,
                    "14":6.5,
                    "15":5.6,
                    "16":4.7,
                    "17":6.9,
                    "18":16.5,
                    "19":5.9,
                    "20":13.3,
                    "21":6.4,
                    "22":10.8,
                    "23":7.5,
                    "24":13.7,
                    "25":9.2,
                    "26":7.7,
                    "27":6.6,
                    "28":13.7,
                    "29":3.6,
                    "30":8.2,
                    "31":24.4,
                    "32":5.7,
                    "33":5.2,
                    "34":20.1,
                    "35":13.5,
                    "36":6.5,
                    "37":18.3,
                    "38":15.1,
                    "39":13.6,
                    "40":36.7,
                    "41":6.0,
                    "42":20.2,
                    "43":31.2,
                    "44":12.0,
                    "45":12.6,
                    "46":7.8,
                    "47":9.1}}];
            
            var max = 40;
            var hoge = Math.floor(255 / 40);

            // 右上にあった常設映画館数というテキストをマウスオーバーすると
            // 映画館数によって都道府県の色が濃くなるよう施す.
            svg.selectAll("text")
                .data(category)
                .enter()
                .append("text")
                .attr("class", "category-label")
                .attr("x", w - 360)
                .attr("y", function(d, i) {
                    return i * 20 + 200;
                })
                .text(function(d) {
                    return d.name;
                })
                .on("mouseover", function(d) {
                    for (var key in d.data) {
                        var color = 255 - hoge * d.data[key];
                        if (color < 0) {
                            color = 0;
                        }
                        d3.select(".pref" + key)
                            .transition()
                            .style("fill", "rgb(" + color + ", " + color + " ,255)");
                    }
                    d3.select(this).style("fill", "#66f");
                })
                .on("mouseout", function(d) {
                    for (var i = 1; i < 48; i++) {
                        d3.select(".pref" + i)
                            .transition()
                            .style("fill", "#fff");
                    }
                d3.select(this).style("fill", "gray");
                });
        </script>
    </body>
</html>

動作確認

$ koko -o

参考URL

PHPで実践する動的計画法

※Qiitaに”動的計画法とメモ化”を説明する記事を書きましたので,こちらにも記載したいと思います.内容は全く同じです(>_<) qiita.com

動的計画法とメモ化

動的計画法(Dynamic Programming)とメモ化(Memorization)は,競技プログラミングなんかでよく使われるアルゴリズムの一緒です. 両者とも計算結果を保存し再利用することで,重複した計算による時間ロスを防ぐものです.

例を示しながら説明したいと思います.

動的計画法 or メモ化を使う動機

まず,フィボナッチ数列とは

n 番目のフィボナッチ数を Fn で表すと、Fn は再帰的に F0 = 0, F1 = 1, F(n + 2) = Fn + (Fn + 1) (n ≧ 0)

で定義される数列です.(Wikipediaより)

標準入力から入力される数字を n とし n 番目(0番目スタート)のフィボナッチ数を求めたい場合どう実装するでしょうか.

(色々な方法はあると思いますが)数学的に一般項を求めようとせず,定義をそのまま使うと,こんな感じの再帰的な処理が書けると思います.

<?php
fscanf(STDIN, "%d", $n);
echo fib($n) . "\n";
function fib($n) {
    if ($n == 0) return 0;
    if ($n == 1) return 1;
    return fib($n - 1) + fib($n - 2);
}

しかし,これだと尋常じゃない時間がかかります. n=4でもこれだけ再帰的にfib関数が呼ばれるんです↓

fib(4) = fib(3) + fib(2)
 = (fib(2) + fib(1)) + (fib(1) + fib(0))

この時間を短縮しくてれるのが動的計画法 or メモ化です! 使いたくなりませんか?w では,実際にコードへ適用していきます.

メモ化でフィボナッチ数を求める

ポイントは$memoListという配列に計算結果を保存しておき,余計な計算を減らすことです. n 番目から計算するトップダウンなイメージです. コードにするとこんな感じです.

<?php
fscanf(STDIN, "%d", $n);
echo fib($n) . "\n";

$memoList;

function fib($n) {
    global $memoList;
    if (isset($memoList[$n])) return $memoList[$n]; // 保存済みならその結果を使う
    if ($n == 0) return 0;
    if ($n == 1) return 1;
    $memoList[$n] = fib($n - 1) + fib($n - 2); // 新たに計算した結果を保存する
    return fib($n - 1) + fib($n - 2);
}

動的計画法でフィボナッチ数を求める

ポイントは$dpという配列で,計算結果を保存するという点ではメモ化と一緒です. ただ,こちらはメモ化とは逆向きに,0番目から計算するボトムアップなイメージです. だから,関数は一度呼ぶだけです.

<?php
fscanf(STDIN, "%d", $n);
echo fib($n) . "\n";

function fib($n) {
    $dp[0] = 0;
    $dp[1] = 1;

    for ($i = 2; $i <= $n; $i++) { // ひたすら計算し,結果を配列に保存していく
        $dp[$i] = $dp[$i - 1] + $dp[$i - 2];
    }

    return $dp[$n];
}

使い分け

• 計算の重複が起きる心配がないか、気にならないほど頻度が低い場 合は、素朴な再帰呼び出しで十分。

• 計算に必要な値がとびとびの場合は、メモ化が計算時間を節約でき て良い。

• 計算に必要な値がぎちぎちの場合は、動的計画法が計算時間を節約 できて良い。

「アルゴリズム」資料 18. 動的計画法とメモ化より) とのことです.

参考文献

動的計画法(京大 マイコンクラブ)

プログラミングコンテストでの動的計画法( Takuya Akibaさん)

いきなりRxAndroid

RxAndroidを使う機会があったので,調べたことをまとめました.
※理解が浅いので間違っている箇所はご指摘いただけると嬉しいです><

RxAndroidとは

Reactive Extentions Androidの略で,RxJavaを内包しAndroidで利用するための機能を追加してあります.

ソースコードはこちらです.

github.com

非同期処理が簡単に実装できます.
AsyncTaskに比べ実装が簡単で,ModelとViewで役割分担しやすいです.

使い方

Observavle:処理を実行しObserverに結果を伝える
Observer:Observavleから結果を受け取り処理する

build.gradle

compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'

MainActivity.java

package himitsu;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.schedulers.Schedulers;
import rx.android.schedulers.AndroidSchedulers;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Observable
            .create(new Observable.OnSubscribe<Integer>() {
                @Override
                public void call(Subscriber<? super Integer> subscriber) {
                    // 重い処理
                    int i = 0;
                    Log.d("debug", "Observable:onNext1");
                    subscriber.onNext(i);
                    Log.d("debug", "Observable:onNext2");
                    Log.d("debug", "Observable:onCompleted1");
                    subscriber.onCompleted();
                    Log.d("debug", "Observable:onCompleted2");
                }
            })
            .subscribeOn(Schedulers.newThread()) // どのスレッドでObservableを走らせるか
            .observeOn(AndroidSchedulers.mainThread()) // どのスレッドでObserverを走らせるか
            .subscribe(new Observer<Integer>() { // Observable.subscribe(observer)すると処理が始まる
                @Override
                public void onCompleted() {
                    Log.d("debug", "Observer::onCompleted");
                }

                @Override
                public void onError(Throwable e) {
                    Log.d("debug", "Observer::onError");
                }

                @Override
                public void onNext(Integer i) {
                    Log.d("debug", "Observer::onNext");
                }
        });
    }
}

Android Monitor

debug: Observable:onNext1
debug: Observable:onNext2
debug: Observable:onCompleted1
debug: Observable:onCompleted2
debug: Observer::onNext
debug: Observer::onCompleted

参考

kirimin.hatenablog.com
使い方やメソッドを丁寧に解説されていて,初心者の私にも分かりやすかったです.

qiita.com
AsyncTaskとの比較が簡潔に記されていました.

[Android] 画像・動画ファイルをアルバムから削除する

サンプルコード

import java.io.File;
import android.content.Context;

String outputPath = <ファイルのパス>;
Context context = this;

File file = new File(outputPath);
if (file.exists()) {
    file.delete(); // ファイル自体の削除
    context.getContentResolver().delete(
        MediaStore.Files.getContentUri("external"),
        MediaStore.Files.FileColumns.DATA + "=?",
        new String[]{outputPath}
    );  // アルバムからの削除
}

説明

ContentProviderはAndroidで他アプリのDBにあるデータを操作できるコンポーネントです. このContentProviderはContentResolverインスタンスで利用できます.このインスタンスはContextクラスのgetContentResolverメソッドで取得できます. 今回はContentResolverのdelete(Uri url, String where, String[] selectionArgs)メソッドで対象の画像・動画を削除しています. MediaStore.Files.getContentUri("external")はファイル一般を全部取得できるURIです.

参考リンク

実験版『人を動かす』

デール・カーネギーさんの名著『人を動かす』を読みました.

名前は知っていたのですが,最近ハマっている漫画『ACMA:GAME』で

主人公が読んでいる本の中にこれを見つけて速攻買いました.

 

人を動かす 新装版

人を動かす 新装版

 

 

まとめ

私たちが相手にするのは

  1. 興味,関心を持ってほしい
  2. 重要な人物として扱われたい
  3. 同情してほしい

と願う感情の動物です.そして,嘘やお世辞に気づく敏感な生き物です.

この本は,そんな相手と付き合うには,想像力を働かせ

相手のことを自分事として考える必要があると教えてくれます.

 

実験

アウトプットを出し身につけるために2つのことを試してみました.

1. 相手は何を求めているのか考えながら話す

これは難しかったです.正直,考えると沈黙ができ会話が弾まなくなるんです.私の頭の回転と社交性の問題もありますが.

そして沈黙に耐えられず,つい自分の話をしてしまいました.つまり失敗です.

失敗を踏まえ,次は段階を設け以下の通りやってみようと思います.

  1. 練習だと意識する
  2. 相手に興味を持って質問する.(長所に気づけると興味を持ちやすい)
  3. 質問攻めにしないよう注意する
  4. 相手の顕在・潜在ニーズを探る(話を聴いてほしい.相談に乗ってほしい.愚痴りたい.意見がほしい.情報がほしい.etc)
  5. それに対し自分ができることを考え発言する

 

2. 相手はみんなVIPだと思い,30分以内の対応を心掛ける

「何かちょっとでも相手のためにしたら感謝してほしい」

「自分の発言に反応してほしい」

「自分の努力や結果に気づいてほしい」

大げさですが,多くの人が多かれ少なかれこういう感情を持っているのではないかと思います.少なくとも私は持っています(笑).

これらの思いに反応するのに精一杯になるのは良くないですが,周囲を気にする余裕を持てる人はカッコいいと思います.だから,意図的に周りを見るようにしました.

具体的には,自分に関わるアクションには30分以内に反応しました.

簡単そうで,一杯一杯になっていたり,重要度が低いと判断するとおざなりになっていました.

ただ,1分1秒を争うことってそんなにないはずで

やってみるとコミュニケーションも円滑に進んでいる気がするので続けたいと思います.

 

振り返り

試してみたものの完璧には程遠いです.

しかし,読んですぐは使いこなせないと思い引き続き実行したいと思います.

効果が出てきたらまたブログに書きます.

非エンジニアの方にSQLを伝える

非エンジニアの方もほしいデータをサクッと出せる環境を作りたい
(社内でHiveQLを簡単に使えるツールができたので、それを利用できたらいいな)
と思っていたところにこんな記事が。

www.slideshare.net


学んだこと3つを書き残しておきます。

1) 伝播
1人に伝授したらその代わりに別の人に広めてもらう、ねずみ講方式ですw
教えることによって学んだことを整理できますし、自分は一人ですけどこの方法だとたくさんの人に伝えられるんですね。
ハッとさせられました。

2) 書いたSQLは公開
初めての人が勉強しやすいですし、同じ悩みで困ることも減るのでいいなと思いました。
公開したSQLを分類・検索しやすい仕組みまで作っとくとより使いやすいと思いました。

3) SQL100本ノック SQL Fiddle
最後にお試し版として10本ノックが付いています。おもしろかったです。
同じ結果を出すにしても、人によってやり方が違うというのを実感しました。
どう書けば処理速度が上がるのかを考える必要性が分かります。
社内でやれば話のタネになるかもと思いました。

以上。Tipsっぽいことを書きましたが
スライドではSQLは導入で、いかに違う職種に越境し
知識創造組織を作るかが書かれていて勉強になりました。

「女子エンジニアのためのDesignワークショップ」に初参加

女子エンジニアのためのDesignワークショップに行ってきました。

recruit-tech.co.jp

普段、開発をしているエンジニア(あるいは未来のエンジニア)が
この日はコードから離れてデザインの勉強をしましょう、というものです。
今回はDesign Girls×リクルートのコラボということで
レイアウトや配色などデザインの基本と、PhotoShopを教わった後
キャリアセッションがありました。

当日の様子はこちら。
#designgirls hashtag on Twitter
#PAAK_JP hashtag on Twitter

感想3つ。
(1) 主催者も内装もすべてがキラキラ
(2) 中だるみがない
(3) ワークショップ後が本番

(1) 主催者も内装もすべてがキラキラ

正直あまり女性限定を意識したことはなかったのですが
内装がかわいかったり、素材がかわいかったり、主催者がかわいかったりして
テンションが上がりました。主催者がかわいいって関係ないだろw
とつっこまれそうですが、仕事をパワフルにやっていて
見た目にも気を使っている人を見るのはモチベーションに繋がりました。

(2) 中だるみがない

長時間の勉強会だと集中力が切れることもあると思います。
このワークショップはBGMが流れていたり
合間にスイーツ休憩やおしゃれランチがあるので気分転換ができます。

(3) ワークショップ後が本番

とまぁここまでメリットばかり書いたので、デメリットとまではいきませんが
自分への戒めの気持ちも込めて。

当日はトピックスが3つもあるので、どれもあくまで入門です。
ただ、導入としてはとても丁寧な上に自習教材も配布されます
自習教材はDesign Girlsのランディングページで実際に利用されている画像やフォントです。
クオリティが高く、作っていて楽しいです♪

#designgirls #PAAK_jp