驚くほど簡単な技術系健忘録

驚くほど簡単な技術系健忘録

アプリやWebサービス、RPAを作る上での健忘録を書いていきます。

機会学習のライブラリはLightGBMを使うのがいいらしい

機械学習ライブラリのおすすめはLightGBMがいいらしいという話を聞いたので、LightGBM/XGBoostをAnaconda経由で簡単にインストールする方法について調べたらいい記事を見つけたので備忘録のために書いておく。
qiita.com

GeoPandasで市区町村別と県別の境界をそれぞれ描く

【手順①:Shapeファイルの用意】
GeoPandasで市区町村別と県別の境界をそれぞれ書くには以下のデータがまず必要となる。
①市区町村別のShapeファイル
②県月のShapeファイル
ということでまず、それぞれのデータをダウンロードしておく

①市区町村別のShapeファイル
「国土数値情報ダウンロード」のデータがやはりいちばん利用しやすい。
以下のURLでデータ形式を「GML(JPGIS2.1)シェープファイル」にして「行政区域(ポリゴン)」から、全国データをダウンロードすると良い。
nlftp.mlit.go.jp
※ファイルはいろいろなところにあるので別のものを使っても大丈夫

②県別のShapeファイル
こちらのものが一番使いやすかった
www.okadajp.org
f:id:nade_nadegata:20200824225009p:plain
この日本全図というのを利用すると日本全国が一気に利用できるので簡単だが東京の小笠原諸島がどうやっても除外できなかったりので、各県のデータをそれぞれダウンロードしてPythonのfor文で読み込むことにした。
※ファイルはいろいろなところにあるので別のものを使っても大丈夫


【手順②:市区町村別に境界を描く】
全部ここのサイトを参考にした
qiita.com

「参考コード」

import numpy as np
import geopandas as gpd
import random
import matplotlib.colors
import matplotlib.pyplot as plt

filename = "/content/drive/My Drive/Colab Notebooks/pref47.geojson"  #自分の場合ここをShapeファイルにしている
df = gpd.read_file(filename, encoding='SHIFT-JIS')

df["target"] = [random.random() for i in range(df.shape[0])]

def colors_scale(arr):
    n_min = min(arr)
    n_max = max(arr)
    cmap = plt.cm.rainbow
    norm = matplotlib.colors.Normalize(vmin=n_min, vmax=n_max)

    arr = [cmap(norm(r)) for r in arr]
    return arr, cmap, norm


mini_df = df[df["N03_001"].isin(["愛媛県", "徳島県", "香川県", "高知県"])]

num_color, cmap, norm = colors_scale(mini_df["num"])

mini_df.plot(color=num_color, figsize=(10,6))
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
plt.colorbar(sm)
plt.show()


【手順③:県別の境界を上から描く】
データで色付けしたり市区町村の境界の上に県の境界を別の色で書く方法がすぐに分からなかったのでこれも備忘録として記載する。

gdf.boundary.plot();

これはGeoPandasの公式ページで見つけた。


【手順④:市区町村名をプロット】

#市区町村の重心を取得
gdf['centroid'] = gdf['geometry'].centroid
gdf["x"] = gdf.centroid.x
gdf["y"] = gdf.centroid.y

#重複しないように市区町村名でグループ化してプロット
name_df = gdf.groupby(['N03_003', 'N03_004'], as_index=False).mean().loc[:, ['N03_003', 'N03_004', 'x', 'y']]
mylist = list(name_df.index)
for l in mylist:
    if name_df['N03_004'][l] == "NO":
        plt.text(name_df['x'][l], name_df['y'][l], name_df['N03_003'][l], fontname="MS Gothic", fontsize=25)        
    else:
        plt.text(name_df['x'][l]-0.02, name_df['y'][l], name_df['N03_004'][l], fontname="MS Gothic", fontsize=25)

これで完成!!!
ということで全体的なソースコードを以下に記載。(多分エラー出ないはず)

import numpy as np
import geopandas as gpd
import pandas as pd
import matplotlib.colors
import matplotlib.pyplot as plt
import glob

#市区町村のshapeデータ
filename = "../data/N03-14_140401.shp"
gdf = gpd.read_file(filename, encoding='SHIFT-JIS')

#県別のshapeデータ
filename = "../data/tokyohk.shp"
pgdf = gpd.read_file(filename, encoding="SHIFT-JIS")
l = glob.glob("../data/*shp")
for filename in l:
    pgdf_temp = gpd.read_file(filename, encoding="SHIFT-JIS")
    pgdf = pgdf.append(pgdf_temp, ignore_index = False)
#重複行を削除す
pgdf.drop_duplicates()

#市区町村の重心を取得
gdf['centroid'] = gdf['geometry'].centroid
gdf["x"] = gdf.centroid.x
gdf["y"] = gdf.centroid.y

#ランダムな数値をカラムに入れる
gdf["target"] = [random.random() for i in range(df.shape[0])]

#色定義
def colors_scale(arr):
    n_min = min(arr)
    n_max = max(arr)
    cmap = plt.cm.rainbow
    norm = matplotlib.colors.Normalize(vmin=n_min, vmax=n_max)
    arr = [cmap(norm(r)) for r in arr]
    return arr, cmap, norm

#特定の県に絞って市区町村境の描画
mini_df = gdf[gdf['N03_001'].isin(["東京都","神奈川県","千葉県","埼玉県"])]
mini_df = mini_df[~mini_df['N03_004'].str.contains("小笠原村|三宅村|八丈町|青ケ島村|御蔵島村|神津島|新島村|利島村|大島町|所属未定地", na=False)]    #島を除外
base = mini_df.plot(color='white', edgecolor='black', figsize=(20,20))  #ベースを白、境界を黒で描画

#県境の描画
mini_df = pgdf[pgdf['PREF'].isin(["東京都","神奈川県","千葉県","埼玉県"])]
mini_df.boundary.plot(ax=base, linewidth=2, edgecolor='red')

#市区町村名をプロット
mini_df = gdf[gdf['N03_001'].isin(["東京都","神奈川県","千葉県","埼玉県"])]
mini_df = mini_df[~mini_df['N03_004'].str.contains("小笠原村|三宅村|八丈町|青ケ島村|御蔵島村|神津島|新島村|利島村|大島町|所属未定地", na=False)]
name_df = mini_df.groupby(['N03_003', 'N03_004'], as_index=False).mean().loc[:, ['N03_003', 'N03_004', 'x', 'y']]
mylist = list(name_df.index)
for l in mylist:
    if name_df['N03_004'][l] == "NO":
        plt.text(name_df['x'][l], name_df['y'][l], name_df['N03_003'][l], fontname="MS Gothic", fontsize=25)        
    else:
        plt.text(name_df['x'][l]-0.02, name_df['y'][l], name_df['N03_004'][l], fontname="MS Gothic", fontsize=25)

#色を描画
num_color, cmap, norm = colors_scale(mini_df["target"])

#カラーバーを記載
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
fig = base.get_figure()
cax = fig.add_axes([base.get_position().x1+0.01, base.get_position().y0, 0.02,
base.get_position().height])
cbar = fig.colorbar(sm, cax=cax)
cbar.ax.tick_params(labelsize=15)

plt.show()

Anacondaばっかり使っていてローカルのPython環境がカオスなことを思い出した

最近Anacondaばっかり使っていたので忘れていたが、ローカルのPython環境は、2系と3系が混じっているし、pip3 install しようとすると管理者権限がないからインストールできないといわれるし、社会人になってすぐに何も理解していない状況でpipとbrewの両方でインストールしているということで完全にbrew関係でインストールしたものを削除しようと思ったけど、PATH関連とかを綺麗にできていない気がするなー。

ということでちょっとネットを調べていたらこんな記事を見つけて勉強になったので備忘録としておいておく。
pyteyon.hatenablog.com

KaggleのRestaurant Revenue Predictionの写経をしてみた

qiita.com

とりあえずこのQiitaの記事を写経しました。

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

import datetime
from sklearn.preprocessing import LabelEncoder

df_train = pd.read_csv("/kaggle/input/restaurant-revenue-prediction/train.csv.zip")
df_test =  pd.read_csv("/kaggle/input/restaurant-revenue-prediction/test.csv.zip")

#再度分解するためにそれぞれのデータにラベルを付ける
df_train['Label'] = "train"
df_test['Label'] = "test"

#目的変数を抽出
revenue = df_train['revenue']
del df_train['revenue']

#前処理がしやすいように、trainとtestをconcat関数で結合(axis=0はユニオン)
#結合関数は pd.concat
df_whole = pd.concat([df_train, df_test], axis=0)

#Open Dateを年・月・日に分解
#applyは関数適用
#lambdaは無名関数
df_whole['Open Date'] = pd.to_datetime(df_whole['Open Date'])
df_whole['Year'] = df_whole['Open Date'].apply(lambda x : x.year)
df_whole['Month'] = df_whole['Open Date'].apply(lambda x : x.month)
df_whole['Day'] = df_whole['Open Date'].apply(lambda x : x.day)

#Cityを数値に変換
le = LabelEncoder()
df_whole['City'] = le.fit_transform(df_whole['City'])

#City Groupを数値に変換 Other -> 0, Big Cities -> 1
#map関数は置換(replace)と似ているがmapだとNANが発生する可能性がある
df_whole['City Group'] = df_whole['City Group'].map({'Other':0, 'Big Cities':1})

#Typeを数値に変換 FC -> 0, IL -> 1, DT -> 2, MB ->3
df_whole['Type'] = df_whole['Type'].map({'FC':0, 'IL':1, 'DT':2, 'MB':3})

#iloc関数を用いて再びtrainとtestに分割
#locはスライス関数スライスする範囲はA:BでAからBという形で引数を指定
"""
del df_whole['Label']
df_train = df_whole.iloc[:df_train.shape[0]]
df_test = df_whole.iloc[df_train.shape[0]:]
"""

#Labelを用いてtrainとtestに分割
df_train = df_whole[df_whole['Label'] == "train"]
df_test = df_whole[df_whole['Label'] == "test"]
del df_train['Label']
del df_test['Label']

#ここから学習させる
from sklearn.ensemble import RandomForestRegressor

#学習に使う特徴量のカラム名を取得
df_train_columns = [col for col in df_train.columns if col not in ['Id', 'Open Date']]

rf = RandomForestRegressor(
    n_estimators=200
    ,max_depth=5
    ,max_features=0.5
    ,random_state=449
    ,n_jobs=-1
)

rf.fit(df_train[df_train_columns], revenue)

prediction = rf.predict(df_test[df_train_columns])

submission = pd.DataFrame({'Id':df_test.Id, 'Prediction':prediction})
submission.to_csv('TFI_submission.csv', index=False)

写経するとよく使う使う関数やデータの成形方法がわかっていい気がする。
コード中のコメントは自分なりの解釈も含んでいる。

写経元のQiitaにもあるように特徴量の取り方とかの工夫をしないと全然スコアは上がらないなーー。

with区とテンポラリーテーブルの差

過去にこんな記事を書きました。
最近思ったのはサブクエリは一度だけ使うけど、テンポラリテーブルは何度も使うときには便利だし、そもそもSQLのコードが見やすいのではないかと思った。

nade-nadegata.hatenablog.jp

そして、一方で、with区とテンポラリーテーブルの違いはなんとなくわかったのでそれについて忘れないうちに記載します。

大きな差は
 ・with区:使うたびに計算するので複数回処理を行う場合には遅い、ただ、最初に構文のチェックをしてくれるのでデバッグには良い。
 ・テンポラリーテーブル;一度作れば何度でも使い回せる、ただし、作るタイミングまで構文ミスがわからない。

ということでそんなに何度も同じテーブルを使い回す複雑な処理をするほど優秀ではないので、自分はwith区結構使っちゃうなーと思った。

【MySLQでテンポラリーテーブル作成コード】
CSVをテーブルするパターン

CREATE TEMPORARY TABLE SalesSummary (
product_name VARCHAR(50) NOT NULL, 
total_sales DECIMAL(12,2) NOT NULL DEFAULT 0.00, 
avg_unit_price DECIMAL(7,2) NOT NULL DEFAULT 0.00, 
total_units_sold INT UNSIGNED NOT NULL DEFAULT 0
);

INSERT INTO SalesSummary
(product_name, total_sales, avg_unit_price, total_units_sold)
VALUES
('cucumber', 100.25, 90, 2);

■とあるテーブルをテンポラリーテーブル化するパターン

CREATE TEMPORARY TABLE C AS
SELECT *
FROM countries
WHERE ranking=1;

SELECT P.name
FROM players AS P
INNER JOIN C ON C.id=P.country_id;


【Verticaでテンポラリーテーブル作成コード】

SELECT CAST(COST as INT) * 2 as COST_APPLE
into local temporary table APPLE on commit preserve rows
FROM  TEST
WHERE NAME = 'りんご'
;

SELECT CAST(COST as INT) * 3 as COST_TOMATO
into local temporary table TOMATO on commit preserve rows
FROM  TEST
WHERE NAME = 'トマト'
;

SELECT SUM(COST) as COST
FROM (
        SELECT COST_APPLE as COST
        FROM APPLE
        UNION ALL
        SELECT COST_TOMATO as COST
        FROM TOMATO
) AS T1
;

最近どっちも中途半端に使っていて、pythonも勉強し始めたので色々と混乱中。

Windows10のAnacondaにconda-forgを用いてgeopandasをインストールする

まず前提として以下2点を注意してください。

1.geopandasを手動でインストールすることは全くおすすめしませんので手動でインストールしたい方は別の記事を探してください。
2.base環境へのインストールは既存パッケージとの依存関係があるためインストールがうまく行かないので注意してください。


以下、手順です。
1.まず以下のコマンドで仮想環境を作りつつ、「jupyter」と「matplotlib」のパッケージをインストールします。

conda create -n myenv jupyter matplotlib

ここでjupyterをインストールしなくてもAnaconda Navigatorからインストールできますが、プロキシがかかっているPCの場合にはここでインストールするのが絶対に良いです。

2.作成した仮想環境を以下のコマンドでアクティブにします。

conda activate myenv

3.conda-forgeからgeopandasをインストールします。

conda install -c conda-forge geopandas

4.conda-forgeからインストールしたrtreeのバージョンが0.9.3でうまく動かないため個別に以下のコマンドで0.9.4にバージョンアップする。
この時注意が必要なのはconda-foreの方ではなんかうまくいかなかったということ。理由は不明です。

conda install -c rtree=0.9.4

これで完成!!!

参考にしたサイトは以下のサイトたちです。
qiita.com

なお、テストはこちらのサイトのコードを使ってテストしました。

import geopandas
import matplotlib.pyplot as plt

world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
print(world.head())
world.plot()
plt.show()

qiita.com



【追記】
参考にプロキシがかかっている社内環境などで開発環境を構築する際に必要な設定に関して以下のブログでまとめられていたのでリンクを張っておきます。
細かい設定などは社内の情報を調べましょう。
qiita.com