Better Engineer Life

記事を頻繁に書き直す性格なのでごめんなさい。

データサイエンティストにデビュー?して思ったこと。ユーザーとの対話が必要。

データサイエンティストというには大げさですが、資料を上司に渡され、ユーザーのデータの可視化、ビジュアライズすることになりました。

表だけではなく、これまでブログに投稿していたような箱ひげ図や棒グラフを作成しました。

面倒なことになりたくないので具体的な業務は話せませんが、やってみて思ったことを話します。

BIツール

BIツールというと、発信力のある会社使われている技術を見ているとTableauが多いのですが、うちの会社が選んだのはQlikViewです。

リコーのような大手でも使われているようですし、QlikView自体はデスクトップで作成した画面がWebでも同様に見れる点はよく出来ているなと思います。

安い製品ではありませんが、これを社内で開発することを考えると元は取れます。

でも、やっぱりこういうのって痒いところに手が届かないよなーと思います。

QlikViewが悪いのではなく、ツールの限界なのかなという気はしています。

Pythonでやりたいように作るのとは勝手が違いました。

私の技術力が足りないのかもしれませんが。

大事なのはコミュニケーション力

綺麗な画面を作ってユーザーに喜んでもらえる自信はあります。だがそれは本来の目的ではありません。

本来の目的であるユーザーが知りたいゴールへ導けるかはわかりません。

データをビジュアライズして見せるだけでは終わりではなく、ユーザーと対話しなければならないかなあという印象です。

可視化してわかりやすくなったとはいえ、ユーザーが箱ひげ図などのチャートを正しく理解出来るかというと補足が必要な気がします。

これに対して、私も可視化しただけでもここから何を知りたいのかは、私は漠然としかわかりません。

いざ仕事でデータビジュアライズをやってみたのに、私のモチベーションが上がりませんでした。その理由はこの点なのかなと思いました。

趣味だったら自分が知りたいデータを集めて、自分が知りたいようにデータを加工してビジュアライズすれば終了です。

ですが、仕事では、ユーザーの存在が欠かせなくなってきます。

  • データ分析側が得意なこと:データに関する知識
  • ユーザーが得意なこと:どのドメインに関する知識

ドメイン知識とデータ知識の二つが相乗効果が必要であり、それを実現するにはユーザーと対話が欠かせません。

ただ、データからわかることを「説明」するだけではなく、もう一歩踏み込んで、ユーザーと「対話」して、より掘り下げていく。

ユーザー側もこんな風に分析すればわかるかなあと漠然と依頼しており、わかったことを踏まえて、更に知りたいことが出てくるでしょうし、本当に知りたいことがわかってくるかと。

そうなると単純に仕様を満たしたシステムを納めるのと比べると終わりがないので、SIerの業態だとビジネスとしてはどこかで区切りをつけないと赤字になってしまいそうです。

もちろん膨大なデータをクリーニングしたり前処理する部分についてはエンジニアリングが求められますが、一般的なソフトウェア開発者よりもコミュニケーション力とかフットワークの良さ、ドメイン知識などが求められる印象です。

これってプログラマーとかエンジニアというよりいわゆるシステムエンジニアとか上流工程をやってきた人が適してる?と感じました。

今回の仕事は、ユーザーとの対話は優しい上司がやって来てくれるので期待です。

おまけ(追記)

作業中は、下記の記事を読んでテンションを上げてました。

blog.btrax.com

100倍とは言いませんが、3倍くらいにはなりました。

quantile(分位数)を学び、箱ひげ図について理解を深める。

Kaggleで他の方のKernelを見ていたら、numpyのquantile関数を使っているのを見かけました。

numpyでは1.15から使える関数なのです。

numpy.quantile

quantileどころか分位数自体、知りませんでした。

分位数とは、結論から言えば、Wikipediaにある

分布をq:1-q に分割する値である。

というのが端的に表しています。

分位数 - Wikipedia

ですが、しっくり来なかったので、Pythonのコードを書きながら、理解を深めて行きました。

import pandas as pd
import numpy as np

df = np.array([1,4,9,16,25])

print('quantile(0.5) =' ,np.quantile(df,0.5)) # 9
print('quantile(0.25) =' ,np.quantile(df,0.25)) # 4
print('quantile(0.75) =' ,np.quantile(df,0.75)) # 16

quantile(0.5)にした場合は中央値となる9が取得されます。 9を境にして、[1,4],[16,25]という0.5:0.5=2:2に集合を分けられます。

同様にquantile(0.25)とquantile(0.75)の場合も1:3ないし、3:1に集合を分割できる値となります。

上記は説明しやすいように都合の良い集合にしていますが、では [1,4,9,16]のように二つに分けられない場合はどうでしょうか。

import pandas as pd
import numpy as np

df = np.array([1,4,9,16])

print('quantile(25) =' ,np.quantile(df,0.25)) # 3.25
print('quantile(50) =' ,np.quantile(df,0.5)) # 6.5
print('quantile(75) =' ,np.quantile(df,0.75)) # 10.75

この場合は、中央値は(4+9) / 2 = 6.5となります。

(4-1) * 0.25 = 0.75より1 * 0.25 + 4 * 0.75 = 3.25

(4-1) * 0.75 = 2.25より9 * 0.75 + 16 * 0.25 = 10.75

となります。

quantileを自分で実装してみる

こんな感じでしょうか。

import math
def my_quantile(array,q):
    array.sort()
    index = (len(array) - 1) * q
    low = math.floor(index)
    high = math.ceil(index)
    return array[low] * (1 - (index-low)) + array[high] * (index-low)

f:id:rimever:20190320200222p:plain

※配列のソートが必要だと気づいたので追記しました。

箱ひげ図

このquantileは箱ひげ図を正しく理解する上で重要です。

箱ひげ図の箱の部分は下が1/4分位数[quantile(0.25)]であり、上が3/4分位数[quantile(0.75)]となり、中央の線は1/2分位数(中央値)となっています。

この箱は値全体の上位25%〜75%を表しているのです。

中央の線は平均ではありません。

f:id:rimever:20190319224258p:plain
中央の線は平均ではありません。中央値です。

ひげの上と下は最大と最小です。

パーセンタイル

分位数を%に置き換えたものです。0.25分位数であれば25パーセンタイルです。これもnumpyの関数として存在します。

import pandas as pd
import numpy as np

df = np.array([1,4,9,16,25,36,49,64,81])

print('percentile(25) =' ,np.percentile(df,25))
print('percentile(50) =' ,np.percentile(df,50))
print('percentile(100) =' ,np.percentile(df,100))

DataFrameにもquantileはある

import pandas as pd
import numpy as np

df = pd.DataFrame(np.array([1,4,9,16,25,36,49,64,81]),
                                    columns=['number'])

print(df['number'].quantile(0.25))

Set関数をプロパティに一括置換する小技

プライベートで10年以上前に自分が書いたソースの移行、リファクタリングなんてやってます。

Pos.SetY(100);

昔の自分はプロパティという概念をよく知らなかった割に、カプセル化にはこだわっていたらしく、Set関数経由で値を割り当てるように指定しています。

元々、プログラミングをJavaC++でスタートしてからC#に取り掛かったため、あまり言語に合わせた書き方を知りませんでした。

単純にYのプロパティにした方が扱いやすいのでこれを機に変更することにしました。

これを一つ一つ手で直してました。置換するにも.SetYに対して、単純に.YとしてもY(100);とコンパイルエラーです。

いざ考えてみると「.SetY(」を「.Y = (」で一括置換にすればいいことに気づいたのです。

Pos.Y = (100);

無駄なカッコが出来ますが、古いソースを移行するときに一番大事なのは大量のコンパイルエラーからビルドしても通る状態に持っていくことです。

f:id:rimever:20190317224945p:plain

それが出来ないとマージはおろか、コミットも出来ませんので。

Pos.AddZ(100);

こんなコードを書いてしまったのも今、後悔しても仕方ないです。これも「.AddZ(」 を「+= (」に置換です。

Pararells Desktopの容量可変ディスクについて

  • Pararells Desktop 14.1.2

Pararells Desktopにハードディスクの設定に容量可変ディスクというオプションがあります。

f:id:rimever:20190303101935p:plain

f:id:rimever:20190317180638p:plain

空き容量がギリギリだったので、これを試してみました。

f:id:rimever:20190317181537p:plain

が、うまく行きませんでした。うーん、なんでだろう。

KB Parallels: What is an Expanding Disk?

最近のインストーラーなどは空き容量をチェックしてしまいますし、容量可変ディスクは諦めた方がいいのかな。

容量可変ディスクをオフにして256GBにしてもダメで、OEMパーティションが妨げになっている模様。

f:id:rimever:20190317183610p:plain

下記の記事は読んでいたのに、自分は回復じゃなくOEMパーティションだから違うなと試してませんでした。

fanblogs.jp

試してみることにします。上記のブログだけで充分だと思いますが、私は下記の記事も参考にしました。

OEMパーティションを削除する方法

  1. ディスクを選択
  2. パーティションを選択

という流れで行います。

考えてみれば当たり前なのですが、パーティションを選ぶ前に、パーティションが属するドライブを選ぶ必要があるわけです。

f:id:rimever:20190317185218p:plain

OEMパーティションといってもdiskpartツールからは回復パーティションだったというわけです。

すると、自動的に拡張されました。

f:id:rimever:20190317201000p:plain
最大容量が256GBに拡張

実体のファイルはそれ相応のファイルサイズになっています。

f:id:rimever:20190317201156p:plain

おまけ

ディスクの管理のコマンドをよく忘れます。

diskmgmt.msc

コマンドで「ディスクの管理」を起動する - Win10jp Windows 10 総合情報サイト Win7からWin10へのPC環境移行連載中!

WPFでウインドウにアイコンを指定する方法

私は、ちょっとしたツールでもアイコンを指定するようにしています。

大袈裟ですが、アイコンを設定することはアプリケーションに命を吹き込むようなことだと考えています。

WPFではよく失敗するので、メモです。

アプリケーションのアイコンを設定する

こちらについては問題ないと思います。WinFormsと同じようにプロジェクトファイルに設定します。

アイコンを設定すれば、自動的に指定したアイコンがプロジェクトフォルダ直下にコピーされます。

f:id:rimever:20190316164529p:plain

あとはビルドすればOKです。

f:id:rimever:20190317091536p:plain

Xamlでウインドウに指定する場合

アプリケーション自体のアイコンとWindowのアイコンは別々で管理しているので、ウインドウにはウインドウで指定することが必要です。

そうしないと、デフォルトのアイコンになってしまいます。

私がよくやるのはリソースファイルにアイコンを追加し、

f:id:rimever:20190316163658p:plain

Xaml

<Window
Icon="Resources/notebook.ico"
/>

と指定する方法ですが、これでは「System.Windows.Markup.XamlParseException」となってしまいます。

f:id:rimever:20190316164529p:plain

インテリセンスも効いて、なぜだと思うのですが、パス形式の指定です。

プロジェクトファイルから見えていても、exeファイルは別のフォルダに生成されるので、指定したアイコンファイルは見えなくなっているからです。

ただリソースに追加しただけでは、アイコンファイル自体のビルドアクションが「なし」になっています。これを「Resource」に変更します。

f:id:rimever:20190316165624p:plain

すると、いけます。

f:id:rimever:20190316170047p:plain

花粉症の時期はいつ? データで見る花粉量

私は花粉症なのですが、どこかの国がPM2.5とか言ってるけど、この花粉は与野党関係なく国を挙げて、解決に進むべきなんじゃないかと思います。

といっても国としても何もしてないわけではなく、下記のサイトでは東京都が花粉情報を公開しています。

www.fukushihoken.metro.tokyo.jp

去年以前の過去のデータもCSVでダウンロードできます。

同じ東京都でも青梅や府中と観測地ごとのデータがあります。観測地ごとで花粉の量は全然違います。

f:id:rimever:20190316141231p:plain
http://www.fukushihoken.metro.tokyo.jp/allergy/pollen/archive/h30/total.html より

特に青梅は抜きん出て花粉量が多いです。

私の勤務地に近そうな千代田区の過去5年分のデータを使って時系列分析してみることにします。

データとしては年ごとに計測された日を計測開始日としていますが、1/10〜5/10に揃えています。

ちなみに私は成人の日を花粉症の薬の飲み始めと決めています。年明けたら病院行かなきゃ〜でいます。有難くない年明けですね。

年ごとに重ねて見た結果が以下になります。

f:id:rimever:20190316134509p:plain

2016,2017は同じくらいです。昨年の2018の花粉の量が凄まじく多いですね。

冬の寒さから暖かくなって花粉の時期が始まってゴールデンウィーク明けたら終わり、梅雨が始まると、漠然と考えてました。

データを見たところでは2月半ば〜4月半ばにかけて花粉が飛ぶようです。また、こうしてみると変動が激しいこともわかります。

風や雨などの天候に左右されるということでしょう。

特に4/1前後がピークで、まさしく桜咲く頃なので花見が出来ませんね。

今日はまだ3月半ば。これから、より一層花粉量が増えるようです。

f:id:rimever:20190316135441p:plain

比較した感じだと2017より、昨年と同じくらいの花粉量になりそうで、今年も辛くなりそうです。

おまけ:大量のzipファイルを一括解凍する方法

データは年ごとにzip形式ですので大量のzipファイルになりますので、解凍するのにはLhaca+というツールを使うと良いです。

f:id:rimever:20190314073241p:plain
大量のzipファイル

ZIPファイルをまとめて解凍するフリーソフト「Lhaca」の使用方法と初期設定

f:id:rimever:20190314073609p:plain
するとデスクトップ上に解凍される

C#はMain関数から始まる

C#でコンソールプロジェクトを作成した時に生成されるのは

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }

です。

f:id:rimever:20190312224837p:plain
.NET Coreでも同じです

Program.csもMain関数もプロジェクトファイルにも記載されていません。

ですがMain関数をエントリポイントとしてプログラムが開始されます。

C#は何を判断基準としてプログラムを実行開始しているのでしょうか。

おまじないだなんだと言われ、触れてはならない聖域のように考えていました。

今回、いたずらをしてファイル名などを変えてみて試してみます。

Program.csをNotProgram.csに変更してみる

問題なく動作します。

f:id:rimever:20190312225105p:plain

Programクラスをstaticクラスにしてみる

問題なく動作します。

f:id:rimever:20190312230850p:plain

staticなMain関数から呼べるのはstaticなメソッドだけです。

Programクラスは潔くstaticクラスにしていい気もします。

Main関数の名前をNotMainに変更してみる

コンパイルエラーになります。

プログラムは、エントリポイントに適切な静的'Main'メソッドを含んでいません。

f:id:rimever:20190312225324p:plain

ダメ元で、Main関数を複数のクラスに持たせてみる

    class Program1
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
            Console.ReadKey();
        }
    }
    class Program2
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
            Console.ReadKey();
        }
    }

やっぱり、コンパイルエラーです。手堅いC#がそんなことを許すわけありませんね。

プログラムで複数のエントリ ポイントが定義されています。エントリ ポイントを含む型を指定するには、/main でコンパイルしてください。

f:id:rimever:20190312230105p:plain

さいごに

C#歴もだいぶ長くなってきて、当たり前を疑ってみました。

XamarinのiOSのプロジェクトを作成すると下記のようになります。

f:id:rimever:20190312224425p:plain

  • Main.cs
  • class Application
  • static void Main(string[] args)

Main.csなのにclassがApplicationというのは嫌だから、変えたいなあと思ったのです。

また、いつものProgram.csもプログラミングだからProgramは間違っていないのですが、個人的にはニュアンスとしてはApplication.csとかの方がいいんじゃないかなあとか思った次第です。