pikesaku’s blog

個人的な勉強メモです。記載内容について一切の責任は持ちません。

グラフデータベースNeo4jのセットアップ

環境構築

 
環境: Mac Sierra
 
Javaインストー

brew cask install java

 
brewでNeo4jをインストー

brew install neo4j

 

やってみる

 
①shellからCypher実行するために設定ファイル変更
stackoverflow.com
 

$ diff /usr/local/Cellar/neo4j/3.2.2/libexec/conf/neo4j.conf /usr/local/Cellar/neo4j/3.2.2/libexec/conf/neo4j.conf_org 
233c233
< dbms.shell.enabled=true
---
> #dbms.shell.enabled=true
235c235
< dbms.shell.host=127.0.0.1
---
> #dbms.shell.host=127.0.0.1
237c237
< dbms.shell.port=1337
---
> #dbms.shell.port=1337

 
上記変更しないと、neo4j-shell実行時に
Connection refused
エラーになる。
 
②neo4j起動

neo4j start

 
※停止はneo4j stop
 
③Webアクセスしneo4jユーザーでログインしパスワード変更
http://localhost:7474/browser/
 
④neo4j-shellログイン確認

$ neo4j-shell

データ構造としてのグラフとは?

グラフとは?

 
ノードとエッジで表現するデータ型
ノードは頂点
エッジはノード間の連結関係を示す
グラフ理論を適用できる
mathtrain.jp

 

グラフの数学的表現方法

以下の2つがあり。
 

①隣接行列

 
以下URLより引用
隣接行列 — WTOPIA v1.0 documentation
 

行列という名の通り, グラフを 2 次元配列で表現する. 配列のインデックスがノードの番号に対応する. 例えば, この 2 次元配列を M とすると, M[i][j] がノード i と ノード j の関係を表す

f:id:pikesaku:20170716223645p:plain

 
連結の向き・重みを考慮した場合の表現もある。
 

②隣接リスト

 
以下URLより引用
mathwords.net
 

f:id:pikesaku:20170716224306p:plain

 

グラフ表現の例

 
tech-blog.abeja.asia
グラフ表現
 
画像や文章の表現も可能。
画像表現に適しているが、グラフ構造を処理するには、計算量が莫大になる問題があり。
 
※Convolution=畳み込み(難しい。。。)
qiita.com

pythonのcymruwhoisモジュールでIPアドレスからwhois情報取得

使い方が簡単

#! /usr/bin/env python3
# -*- coding: utf-8 -*-


from cymruwhois import Client

# 一個だけデータを取得する場合
c = Client()
r = c.lookup('8.8.8.8')
print('ANS:    ' + r.asn)
print('IP :    ' + r.ip)
print('PREFIX: ' + r.prefix)
print('CC:     ' + r.cc)
print('OWNER:  ' + r.owner)

# 複数データを取得する場合

# 悪い例
# 同じCIDR内だが、都度クエリが発生
# 以下URLにも注意書きがあり。loop内で使うと性能悪い
# http://pythonhosted.org/cymruwhois/api.html
for i in range(256):
    ip = '8.8.8.' + str(i + 1)
    r = c.lookup(ip)
    print(r.asn)


# 良い例
# 同じCIDR内なので、通信は最初の一回だけ。
ips = list()
for i in range(254):
    ips.append('8.8.8.' + str(i + 1))

for r in c.lookupmany(ips):
    print(r.asn)


信頼性もありそう

・CentOS7のwgetコマンドは引数に指定されたIPを管理するレジストラを探すためにDNS(逆引き&TXTレコード)を使っている。
 その問い合わせ先がcymruのサーバであった。ここでIPアドレスを管理するレジストラ情報を得て、whoisプロトコルで情報を得ている。
 →CentOSwgetに組み込まれているサービスに関与している組織。

・cymruとは?
 
www.team-cymru.org

 Googleや名だたるITサービスプロバイダーがパートナー

備考

whoisはレスポンスの規則がないとの情報あり。RDAPなるものを知り、これがいいと思ったがCentOS7環境でRDAP対応のipwhoisが上手く動かず、困っていたところで見つけたモジュール。
Macではipwhois正常に動く。。。使いやすメリットでcymruwhoisを使おう。cymruwhoisはRDAP未対応?(デフォルト動作ではwhoisプロトコルを利用)

jubatus anomalyのnum_rules動作検証(jubaanomalyにデータを投入するツール)

説明

jubaanomalyにデータを学習させたり、外れ値を計算させるツール。

指定可能なオプションは以下の通り。

オプション 意味
-t num_rulesのタイプ(num or str or log)を指定。必須指定オプション
-c データ学習前に既存データをクリアする。省略可能。デフォルトは無効
-s 学習データをファイルに保存する。省略可能。デフォルトは無効
-d 学習済みデータのID一覧を出力する。省略可能。デフォルトは無効
-o データを学習せず外れ値の計算だけやる。省略可能。デフォルトは無効

-dは-c,-s,-o,-tと排他
-sは-oと排他

anom_test.py

# -*- coding: utf-8 -*-

import signal
import argparse
import numpy as np
import matplotlib.pyplot as plt

from jubatus.anomaly import client
from jubatus.common import Datum

parser = argparse.ArgumentParser(description='This is jubaanomaly test tool')
parser.add_argument('name',                                help='data name')
parser.add_argument('-t', '--type',      dest='type',      help='type num_rules')
parser.add_argument('-c', '--clear',     dest='clear',     help='clear data',     action="store_true", default=False)
parser.add_argument('-s', '--save',      dest='save',      help='save or not',    action="store_true", default=False)
parser.add_argument('-d', '--display',   dest='display',   help='save or not',    action="store_true", default=False)
parser.add_argument('-o', '--only_calc', dest='only_calc', help='only calc',      action="store_true", default=False)

args = parser.parse_args()


def display_result(rdata, idata):
    dpoint = 5
    idx = 0
    result = dict()
    inf = float("inf")
    for i in idata:
        print(i, rdata[idx])
        if rdata[idx] != inf:
            result[i] = round(rdata[idx], dpoint)
        idx += 1

    plt.plot(list(result.keys()), list(result.values()), 'o')
    plt.title(u'Outliers')
    plt.show()


def connect_srv():
    try:
        anom = client.Anomaly("127.0.0.1", 9199, args.name)
        if args.clear:
            anom.clear()
    except:
        err_fin('failed to connect to server')
    return anom


def study_data(anom):
    idata = list()
    rdata = list()
    while True:
        try:
            line = float(input().strip())
            idata.append(line)
        except EOFError:
            break

        datum = Datum()
        if args.type == 'num':
            d_type = 'd1'
        elif args.type == 'str':
            d_type = 'd2'
        elif args.type == 'log':
            d_type = 'd3'
        datum.add_number(d_type, line)
        if args.only_calc:
            ret = anom.calc_score(datum)
#            print(ret)
        else:
            ret = anom.add(datum)
            #if (ret.score != float('Inf')) and (ret.score != 1.0):
            print(ret)
        rdata.append(ret)

    if args.save:
        anom.save(args.name)
    return rdata, idata


def do_exit(sig, stack):
    print('You pressed Ctrl+C.')
    print('Stop running the job.')
    exit(0)


def display_data(anom):
    try:
        data = anom.get_all_rows()
    except:
        err_fin(args.name + ' failed to get data')
    if not data:
        print(args.name + ' has no data')
    else:
        for i in sorted(data, key=float):
            print(i)


def err_fin(mes):
   print('Error: ' + mes)
   exit(1)


def chk_args():
    # -dと-c,-s,-n,-oは排他
    type_rules = ['num', 'str', 'log']
    if args.display and (args.clear or args.save or args.type or args.only_calc):
        err_fin('can not use -d with -c,-s,-o,-t')
    if args.save and args.only_calc:
        err_fin('can not use -s with -o')
    if not args.display and not args.type in type_rules:
        err_fin('invlid rule_type. ' + 'should be ' + ', '.join(type_rules))


if __name__ == '__main__':
    signal.signal(signal.SIGINT, do_exit)
    chk_args()
    anom = connect_srv()
    if args.display:
        display_data(anom)
        exit()
    else:
        rdata, idata = study_data(anom)
        if args.only_calc:
            display_result(rdata, idata)

使い方例

データを初期化してから学習させる(num_rulesのtypeはstr、学習データのファイル保存なし)

10個のデータを学習

$ seq 1 10 | python ./anom_test.py -c -t str test
id_with_score{id: 220, score: inf}
id_with_score{id: 221, score: 1.0}
id_with_score{id: 222, score: 0.9926380515098572}
id_with_score{id: 223, score: 1.0}
id_with_score{id: 224, score: 1.0065279006958008}
id_with_score{id: 225, score: 1.010113000869751}
id_with_score{id: 226, score: 0.9977192282676697}
id_with_score{id: 227, score: 0.993184506893158}
id_with_score{id: 228, score: 0.9930071830749512}
id_with_score{id: 229, score: 0.9959399700164795}

学習済みデータの確認

$ python ./anom_test.py -d test
220
221
222
223
224
225
226
227
228
229
$

外れ値計算 & 分布図出力

$ seq 1 10 | python ./anom_test.py -o -t str test
1.0 0.9999997615814209
2.0 0.9999999403953552
3.0 1.0
4.0 0.9999997615814209
5.0 0.9999998211860657
6.0 1.0
7.0 1.0
8.0 0.9999999403953552
9.0 1.0
10.0 1.0

f:id:pikesaku:20170709223633p:plain

jubatus anomalyのnum_rules動作検証(テストデータ分布状況出力ツール)

説明

データを受けてヒストグラムとパイチャートを出力
一次元データのみ対応。

dist_disp.py

# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt


def make_pie_data(data):
    pie_data = dict()
    for i in data:
        pie_data[i] = data.count(i)
    return pie_data


def display_dist(data):
    # ヒストグラム生成
    plt.subplot(2, 1, 1)
    plt.hist(data, bins=50)
    plt.title(u'Input data histgram')

    # パイチャート生成
    pie_data = make_pie_data(data)
    plt.subplot(2, 1, 2)
    plt.pie(list(pie_data.values()), labels=list(pie_data.keys()))
    plt.title(u'Input data pie chart')

    # タイトルの被りを防ぐ
    plt.tight_layout()
    plt.show()


def get_data():
    data = list()
    while True:
        try:
            line = float(input().strip())
            data.append(line)
        except EOFError:
            break
    return data


if __name__ == '__main__':
    data = get_data()
    if data:
        display_dist(data)

使い方例

$ (seq 1 10;echo 1; echo 3) | python ./disp_dist.py

f:id:pikesaku:20170709214433p:plain

jubatus anomalyのnum_rules動作検証(テストデータ生成ツール)

説明

引数にデータ数、データのタイプ(以下参照)を指定し、データを生成するツール
・全部同じデータ
・一様分布
・標準正規分布
正規分布(平均50、標準偏差15)

make_data.py

# -*- coding: utf-8 -*-

import argparse
import numpy as np

parser = argparse.ArgumentParser(description='make data')
parser.add_argument('num_of_data',               help='num of data', type=int)
parser.add_argument('-t', '--type', dest='type', help='distribution type. zenbu_onaji or seiki1 or seiki2 or ichiyou', required=True)
args = parser.parse_args()


# 標準正規分布
def seiki1():
    for i in list(np.random.randn(args.num_of_data)):
        print(i)


# 正規分布
def seiki2():
    heikin = 50
    hensa = 15
    for i in list(np.random.normal(heikin, hensa, args.num_of_data)):
        print(i)


# 一様分布
def ichiyou():
    s = 1
    e = 100
    for i in list(np.random.uniform(s, e, args.num_of_data)):
        print(i)


# 全部同じ値
def zenbu_onaji():
    for i in range(args.num_of_data):
        print(1)


if __name__ == '__main__':
    if args.type == 'zenbu_onaji':
        zenbu_onaji()
    elif args.type == 'seiki1':
        seiki1()
    elif args.type == 'seiki2':
        seiki2()
    elif args.type == 'ichiyou':
        ichiyou()
    else:
        print('Error: invalid type')
        exit(1)

使い方例

$ python ./make_data.py 5 -t zenbu_onaji
1
1
1
1
1
$ python ./make_data.py 5 -t ichiyou
82.293075475
87.1943748063
84.6043656292
49.5115604224
90.631604143
$ python ./make_data.py 5 -t seiki1
-0.120117893927
0.786911376173
-1.0530541462
-0.3343056616
1.43960974838
$ python ./make_data.py 5 -t seiki2
35.7171926461
61.4762823277
56.6341546999
36.3448121399
63.7552040148
$ 

jubatus anomalyのnum_rules動作検証

目的

jubatus anomalyのnum_rules(typeがstr or num or log)の違いによる動作確認をする。

num_rulesのtypeの説明

データ変換 — Jubatus

上記URLによると以下の通り

意味
num 与えられた数値をそのまま重みに利用する。
str 与えられた数値を文字列として扱う。これは、例えばIDなど、数値自体の大きさに意味のないデータに対して利用する。重みは1とする。
log 与えられた数値の自然対数を重みに利用する。但し、数値が1以下の場合は0とする。

作成したツール

①make_data.py

データを生成する

pikesaku.hatenablog.com

②dist_disp.py

データの分布状況をヒストグラム・パイチャートで出力する

pikesaku.hatenablog.com

③anom_test.py

jubaanomalyにデータを投入する

pikesaku.hatenablog.com