フリーランス 技術調査ブログ

フリーランス/エンジニア Ruby Python Nodejs Vuejs React Dockerなどの調査技術調査の備忘録

Nisikaの中古マンション価格予測のデータをグラフで可視化する

はじめに

  • 下記の記事でデータ前処理を行ったので、今回は、そのデータ前処理を行ったデータをグラフで確認する px-wing.hatenablog.com

ヒストグラム表示

fig, axes = plt.subplots(2,2, figsize=(20,10))
axes[0][0].hist(df["最寄駅:距離(分)"],bins=20)
axes[0][1].hist(df["面積(㎡)"],bins=200)
axes[0][1].set_xlim(0, 250)
axes[1][0].hist(df["建築年"],bins=20)
axes[1][1].hist(df["取引価格(総額)_log"],bins=20)
plt.show()

f:id:PX-WING:20220223205051p:plain

散布図

  • 散布図の見方はこちらが参考になります。 next-sfa.jp
fig, axes = plt.subplots(3,1, figsize=(20,10))
axes[0].scatter(df["最寄駅:距離(分)"],df["取引価格(総額)_log"], alpha=0.1, color="red")
axes[0].set_xlabel("Distance (in minutes)", fontsize=20) # (6)x軸ラベル
axes[0].set_ylabel("Transaction price (total)", fontsize=20) # (7)y軸ラベル
axes[1].scatter(df["面積(㎡)"],df["取引価格(総額)_log"], alpha=0.1, color="red")
axes[1].set_xlabel("Area (m2)", fontsize=20) # (6)x軸ラベル
axes[1].set_ylabel("Transaction price (total)", fontsize=20) # (7)y軸ラベル
axes[2].scatter(df["建築年"],df["取引価格(総額)_log"], alpha=0.1, color="red")
axes[2].set_xlabel("Year of construction", fontsize=20) # (6)x軸ラベル
axes[2].set_ylabel("Transaction price (total)", fontsize=20) # (7)y軸ラベル
plt.show()

f:id:PX-WING:20220223205241p:plain

相関関係の確認

  • 説明変数どうしの、相関がありすぎるものが混ざっていると推定精度が悪化する可能性があるので、目視でチェックする
df[['最寄駅:距離(分)','面積(㎡)','建築年', '取引価格(総額)_log']].corr()

## 下記は上記の出力結果
    最寄駅:距離(分) 面積(㎡) 建築年   取引価格(総額)_log
最寄駅:距離(分) 1.000000    0.151880    0.117809    -0.215520
面積(㎡) 0.151880    1.000000    -0.067415   0.382755
建築年   0.117809    -0.067415   1.000000    -0.538493
取引価格(総額)_log    -0.215520   0.382755    -0.538493   1.000000

ヒートマップの出力

#日本語の文字化け対応※うまくいかない
#import matplotlib
#matplotlib.rcParams["font.family"] = "AppleGothic"
## 利用できるフォントの種類の一覧が表示することができる
#matplotlib.font_manager.fontManager.ttflist 

sns.heatmap(df[['最寄駅:距離(分)','面積(㎡)','建築年', '取引価格(総額)_log']].corr())

f:id:PX-WING:20220223205509p:plain

棒グラフの表示

df["取引年"] = df["取引時点"].apply(lambda x:str(x)[:4])
fig, axes = plt.subplots(2,1, figsize=(20,10))
sns.countplot(x="取引年", data=df.sort_values("取引年"), ax=axes[0])
sns.countplot(x="取引時点", data=df.sort_values("取引時点"), ax=axes[1])

f:id:PX-WING:20220223211542p:plain

Nisikaの中古マンション価格予測でデータ前処理をしてみる

はじめに

  • 下記のコンペデータを利用してデータの前処理をしてみる www.nishika.com

google colabを利用して検証する

  • サイトから検証データ、テストデータをダウンロードし、google colabにアップロードする。
  • 前処理を行うために必要なライブラリをインストールする
  • 初期値を設定する
# trainデータを解凍する
!unzip train.zip
## 和暦を西暦に変換するライブラリ
!pip install jeraconv

import datetime

currentDateTime = datetime.datetime.now()
date = currentDateTime.date()
current_year = date.strftime("%Y")
  • 標準でインストールされているライブラリをインポートする
## 必要なライブラリーのimport
import glob
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

データの読み込み

  • 訓練データが複数のCSVファイルに分かれているので1つにDataFrameにまとめる
  • データ量や欠損値データの確認を行う
files = glob.glob("train/*.csv")

data_list = []
for file in files:
  # trainフォルダにあるすべてのCSVファイルをdata_frameに変換し、その値を配列に格納する
  # index_col=0のようにindexとして使いたい列の列番号を0始まりで指定する。
  data_list.append(pd.read_csv(file, index_col=0))

# 配列に格納したデータフレームを1つにまとめる
df = pd.concat(data_list)

# データの行数とカラム数の確認
df.shape

# 欠損値やデータ型の確認
# 0 non-nullのデータは欠損値のため、削除する
df.info()

欠損値データの削除

## 欠損値のカラムの削除
#for col in df.columns:
#  if df[col].count() == 0:
#    df = df.drop(col, axis=1)
nonnull_list = []
for col in df.columns:
  count = df[col].count()
  if count == 0:
    nonnull_list.append(col)
df = df.drop(nonnull_list, axis=1)    
df.info()

特徴量パラメータの加工

市区町村データの削除

  • 市区町村名と市区町村コードは、一致するデータが入っている。 全く一致するデータが入っていることはモデルとしてよくない。 全く一致するようなデータが入っていると多重共線性が発生する。

※多重共線性とは、言ってみれば類似度の高い説明変数の間で回帰係数の「取り合い」のような現象が発生しているケースです。

# value_countsは一意の値のカウントを含むシリーズを返します。同じ結果となる場合、多重共線性が発生する可能性がたかいデータとして想定できるのでカラムを削除する
df["市区町村コード"].value_counts()
#df["市区町村名"].value_counts()
df = df.drop("市区町村名", axis=1)

種類データの削除

## describeはint型、float型のデータの統計量を返す。object型は表示されない
df.describe()
## 文字列型の統計量のデータを参照したい場合
## 文字列のデータを確認しuniqueで表示されるデータが1件の場合、すべて同じデータなので意味のなさないデータだとわかる
## そのため、種類のカラムを削除する
df.astype("str").describe()
df = df.drop("種類", axis=1)

最寄駅:距離(分)データの加工

## 数字に文字列が入ってデータを加工する
pd.set_option("display.max_rows", 500)
## 分とかHなどの文字が入っていることを確認する
df["最寄駅:距離(分)"].value_counts()
## 対象の文字列を数値に変換するリストを作成する
dis = {
    "30分?60分": 45,
    "1H?1H30": 75,
    "2H?": 120,
    "1H30?2H": 105
}
## 上記のリストの内容に変換し、float型に変換して再設定する
df["最寄駅:距離(分)"] = df["最寄駅:距離(分)"].replace(dis).astype(float)

面積の加工

pd.set_option("display.max_rows", 500)
df["面積(㎡)"].value_counts()
df["面積(㎡)"] = df["面積(㎡)"].replace({"2000㎡以上": "2000"}).astype(float)
df["面積(㎡)"].value_counts()

建築年の加工

  • jeraconv ライブラリを利用して和暦を西暦に変換する
  • 上記で算出した値から現在の年を引くことで築年数を算出する
## 和暦のデータを西暦に変換して現在からの経過年数を格納する
from jeraconv import jeraconv
j2w = jeraconv.J2W()

#df["建築年"].valueでループすることも可能だが1行づつ全データに対して置換をしないといけないので時間がかかる
y_list = {}
for i in df["建築年"].value_counts().keys():
  try:
    tikunensu = int(current_year) - j2w.convert(i)
  except:
    ## 戦前という文字列は置換できないので、固定値で
    tikunensu = int(current_year) - 1945
  finally:
    y_list[i] = tikunensu
df["建築年"] = df["建築年"].replace(y_list)

取引時点の加工

##  取引時点
year = {
    "年第1四半期": ".25",
    "年第2四半期": ".5",
    "年第3四半期": ".75",
    "年第4四半期": ".99",
}
year_list = {}
year_replace = ""
for i in df["取引時点"].value_counts().keys():
    for k, j in year.items():
       if k in i:
           year_replace = i.replace(k,j)
    year_list[i] = year_replace

df["取引時点"] = df["取引時点"].replace(year_list).astype(float)

加工後のデータ

 市区町村コード   最寄駅:距離(分) 面積(㎡) 建築年   建ぺい率(%)   容積率(%)  取引時点    取引価格(総額)_log
count   637351.000000   614306.000000   637351.000000   619117.000000   614848.000000   614848.000000   637351.000000   637351.000000
mean    18513.985300    11.731487   58.663570   26.978080   67.601944   301.601876  2013.633153 7.217424
std 9596.722442 12.197090   26.712019   11.496701   10.402295   148.105400  3.884546    0.353935
min 1101.000000 0.000000    10.000000   2.000000    30.000000   50.000000   2005.750000 2.653213
25% 13106.000000    5.000000    45.000000   18.000000   60.000000   200.000000  2010.500000 7.000000
50% 14104.000000    8.000000    65.000000   26.000000   60.000000   200.000000  2013.750000 7.255273
75% 27114.000000    14.000000   75.000000   35.000000   80.000000   400.000000  2016.990000 7.447158
max 47213.000000    120.000000  2000.000000 77.000000   80.000000   1300.000000 2019.990000 9.934498

pandasとMatplotlibを動画で学習

はじめに

  • 下記の動画でpandasとMatplotlibの勉強をする

www.youtube.com

www.youtube.com

pandas

  • pandasで勉強したこと
# データの数
df.shape

df.info()
df.columns
# pclassのユニークの値取得
df["pclass"].unique()
## pclassのカテゴリごとの数
df["pclass"].value_counts()
# データの件数、平均値、最小値、最大値取得
df.describe()
## survivedを基準とした各カラムの平均値を算出
df.groupby("survived").mean()
##  データの降順ソート
df.sort_values("age", ascending=False)
## 欠損値の数
df.isnull().sum()

## 欠損値の行を削除する。行の中で1つでもNULLのカラムがあった行データは削除する
df.dropna()
## 欠損値の補完
df["age"].fillna(df["age"].mean()).head(10)

## 文字列データのダミー変数化
df= pd.get_dummies(df)

## 各カラムの相関値の取得
df.corr()

## indexの値をカラムとして利用できるようにする
df = df.reset_index()
df_a = df[["index", "survived"]]
df_b = df[["index", "pclass"]]

## データフレームの結合①
## index(onパラメータ)をキーとしてinnerJoin(howパラメータ)をする
pd.merge(df_a, df_b, on="index", how="inner")

## データフレームの結合②
## axis=1の場合、横に結合、axis=0の場合、縦の結合になる
pd.concat([df_a, df_b], axis=1)

Matplotlib

  • matplotlibで勉強したこと
## ヒストグラム
##bins  ビン (表示する棒) の数。階級数。(デフォルト値: 10)
##range ビンの最小値と最大値を指定。(デフォルト値: (x.min(), x.max())) 
plt.hist(df["fare"], bins=40, range=(0,100))
plt.show()

## 折れ線グラフ
### figure グラフのサイズを指定する
plt.figure(figsize=(20,10))
### グラフのラベルを表示
plt.title("test")
plt.xlabel("data_index")
plt.ylabel("farel")

plt.plot(df["fare"])
plt.show()

### 性別ごとのfareの値をヒストグラムで表示
df["fare"].hist(by=df["sex"])


## ヒストグラムのグラフを複数表示する
fig, axes = plt.subplots(2,2,figsize=(20,10))
axes[0][0].hist(df["age"], bins=20)
axes[0][1].hist(df["fare"], bins=20)
axes[0][1].set_xlim(0,200)
axes[1][0].hist(df["sex"], bins=20)
axes[1][1].hist(df["pclass"], bins=20)

## 棒グラフ
df.groupby("pclass").count().index
### pclass毎のsurviedの数
df.groupby("pclass").count()["survived"]

plt.bar(df.groupby("pclass").count().index, df.groupby("pclass").count()["survived"])

## 散布図
plt.scatter(df["fare"], df["age"], alpha=0.2)

pandasの20本ノックをやってみた。

はじめに

  • 下記の動画の20本ノックを試してみた。 www.youtube.com

20本ノックを試した結果

import pandas as pd

# 1本目 データの読み込み
df = pd.read_csv("./weather.csv");

# 2本目 データの中身の確認(先頭3行、末尾10行を表示)
df.head(3)
df.tail(10)

# 3本目 不要な列、特定の行の削除
df.columns

## 下記の方法は必要な列だけを指定してデータを抽出する方法
df = df[['年月日', '平均気温(℃)', '最高気温(℃)','最低気温(℃)','降水量の合計(mm)','最深積雪(cm)','平均雲量(10分比)','平均蒸気圧(hPa)','平均風速(m/s)','日照時間(時間)']][1:]
df
# 不要な列の削除は下記のようにdropでもできる。 axis=1は行、axis=0は列
#df = df.drop(["日照時間(時間).1","日照時間(時間).2"],axis=1)
#df

# 列の削除
#df = df.drop(["年月日","最高気温(℃)"],axis=1)
#df
# 行の削除
#df = df.drop(3,axis=0)
#df

# 4本目 データの型、サイズ、列名、行名の確認
# データ型の確認
df.dtypes
# データのサイズの確認
df.shape
# 列名の確認
df.index
# 列名確認
df.columns

# 5本目 任意の要素を取得する
# 自分の答え
#df.loc[5:10]
#df.loc[3:6][['最高気温(℃)','最低気温(℃)','降水量の合計(mm)','最深積雪(cm)']]

# ilocを利用する場合
# indexは0スタートとなるため、5行目のデータを取得する場合、4を指定する、カラム名もindexで指定する
df.iloc[4:10, 2:6]

# locを利用する場合
# 5行目から10行目を取得して、カラム名が「最高気温(℃)」~「最深積雪(cm)」を指定する 
df.loc[5:10, '最高気温(℃)':'最深積雪(cm)']

# 6本目 条件抽出
# nationalityがAmericaである
# ageが20以上30未満である
# 自分の答え
df_peple = pd.read_csv("./people.csv")
df_peple.query('20 <= age < 30 and nationality == "America"')

## 別の書き方
df_peple[df_peple['nationality'] == 'America']
df_peple[df_peple['nationality'].isin(['America'])]
df_peple[(df_peple['age'] >= 20) & (df_peple['age'] < 30)]
df_peple.query('age >= 20 & age < 30')

# 7本目 ユニークな値を抽出
## pandas.core.frame.DataFrameの場合、uniqueは使えない。
## pandas.core.series.Seriesの場合、uniqueが使える
df_peple['nationality'].unique()

# 8本目 重複除去
df_peple.drop_duplicates(subset='nationality')

# 9本目 カラム名変更

## 一括でカラム名を変更する場合
df.columns = ['年月日', '平均気温', '最高気温', '最低気温', '降水量の合計', '最深積雪',
              '平均雲量', '平均蒸気圧', '平均風速', '日照時間']

## 特定のカラム名を変更する場合
df.rename(columns={'平均気温' : '平均'})

# 10本目 並び替え
## 昇順
df.sort_values('最高気温')

## 降順にする場合、ascending=Falseを指定する
df.sort_values('最高気温', ascending=False)

## 11本目 ダミー変数への処理

## 文字列の値を数値に変換
pd.get_dummies(df_peple['nationality'])

## データフレームに結合したい場合
pd.get_dummies(df_peple, columns=['nationality'])

## 12本目 欠損値の確認
df.isnull()
df.isnull().sum()

## 13本目 欠損値の補完
df = df.fillna(0)
## https://qiita.com/0NE_shoT_/items/8db6d909e8b48adcb203
df

Android Studioでemulatorが起動しなくなった

はじめに

  • 2週間ぶりにAndroid Studioで開発をしようとしたらemulatorを起動したところ、下記のエラーが発生したので、調査してみて。

エラーの内容

WARNING | unexpected system image feature string, emulator might not function correctly, please try updating the emulator. VK_VERSION_1_0 check failed: vkCreateInstance not found VK_VERSION_1_0 check failed: vkEnumerateInstanceExtensionProperties not found VK_VERSION_1_0 check failed: vkEnumerateInstanceLayerProperties not found createOrGetGlobalVkEmulation: Warning: Vulkan 1.0 APIs missing from instance INFO | Android emulator version 31.2.8.0 (build_id 8143646) (CL:N/A)

下記の方法を試したけどダメだったこと

対応方法

  • C:\Users\user\.android\advancedFeatures.iniのファイルを作成して下記の記述を追記するだけで起動することができました。
Vulkan = off
GLDirectMen = on

FlutterでRiverpodを使用してみた

Riverpodとは

状態管理ライブラリです。

Providerの種類

種類 説明
Provider 定数
StateProvider 変数・標準
ScopedProvider 出力を指定する
StateNotifierProvider メソッド付き
FutureProvider Future版
StreamProvider Stream版
ChangeNotifierProvider ChangeNotifierを使う

Consumerの種類

種類 説明
Consumer Widgetに埋め込む
ConsumerWidget StatelessWidgetのRiverpod版
ConsumerStatefulWidget StatefulWidgetのRiverpod版
簡単に実装できるが、パフォーマンスはあまりよくない

表示方法

表示方法 説明
watch 状態変更でWidgetを更新
read 状態変更でWidgetを更新しない
select データ内の特定の値が変更したときのみWidgetを更新する

ConsumerWidget

  • ステータスが更新されるたびに画面全体がリロードされてしまう。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'provider.dart';

void main() {
  runApp(
    // Adding ProviderScope enables Riverpod for the entire project
    const ProviderScope(child: MyApp()),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  MyHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    print("MyHomePage rebuild");
    return Scaffold(
      appBar: AppBar(
        title: Text(ref.watch(titleProvider)),
      ),
      body: Center(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              ref.watch(messageProvider)
            ),
            Text(
              // ref.watch(countProvider.state).state.toString()
              // ref.watch(countProvider).toString()
              // ref.watch(countProvider.notifier).state.toString()
              ref.watch(countProvider.state).state.toString(),
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.watch(countProvider.state).state ++,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
import 'package:flutter_riverpod/flutter_riverpod.dart';

final titleProvider = Provider<String>((ref) {
  return 'Riverpod Demo Home Page';
});

final messageProvider = Provider<String>((ref) => 'テストテストテストメッセージが入ります');

final countProvider = StateProvider<int>((ref) => 1);

Consumer

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'provider.dart';

void main() {
  runApp(
    // Adding ProviderScope enables Riverpod for the entire project
    const ProviderScope(child: MyApp()),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  MyHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    print("MyHomePage rebuild");
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Consumer(
          builder: (BuildContext context, WidgetRef ref, Widget? child) => Text(
            ref.watch(titleProvider)
          ),
        ),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Consumer(
              builder: (context, ref, child) => Text(
                  ref.watch(messageProvider)
              ),
            ),
            Consumer(
              builder: (BuildContext context, WidgetRef ref, Widget? child) {
                return Text(
                  // ref.watch(countProvider.state).state.toString()
                  // ref.watch(countProvider).toString()
                  // ref.watch(countProvider.notifier).state.toString()
                  ref.watch(countProvider.state).state.toString(),
                  style: Theme.of(context).textTheme.headline4,
                );
              }),
          ],
        ),
      ),
      floatingActionButton: Consumer(
        builder: (context, ref, child) {
          print('button rebuild');
          return FloatingActionButton(
            onPressed: () => ref.read(countProvider.state).state ++,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          );
        },
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

pythonのbeautifulsoup4を利用してサイト内にあるすべてのリンクを抽出してみる

はじめに

  • 運用しているサイトでバックアップファイルが多く残っているサイトで、現時点でトップページから正しくリンクが張られているページを調査するため、手動でチェックするのは、しんどいのでプログラムで調査できないか、サンプルのプログラムを作成してみました。

利用したパッケージ

beautifulsoup4
requests

サンプルコード

from html.parser import HTMLParser
from bs4 import BeautifulSoup
import requests

baseurl = "<調査したいURL>";
urlList = []
ok_urlList = []

def searchLink(url):
    global urlList
    global ok_urlList
    if url == "":
        response = requests.get(f"{baseurl}index.php")
    else:
        response = requests.get(f"{url}")

    html = BeautifulSoup(response.text, 'html.parser')
    for link in html.findAll("a"):
        href = link.get('href')
        if href is None:
            pass
        else:
    # hrefがないAタグや、index.htmlがある場合は省く
            if ("http" not in href and "index.html" not in href):
                href = href.replace('../', '')
                href = href.replace('./', '')
                href = href.replace('//', '')
     # 拡張子がphpとhtmlファイルのURLだけ取得する
                if (".php" in href or ".html" in href ):
                    if href[-1] == "/":
                        urlList.append(f"{baseurl}{href}index.php")
                    else:
                        if href[0] == "/":
                            urlList.append(f"{baseurl}{href[1:]}")
                        else:
                            urlList.append(f"{baseurl}{href}")

  # 配列に格納されている値をユニークにする
    urlList = list(dict.fromkeys(urlList))
 # チェック済みのURLは省く
    result = list(set(urlList) - set(ok_urlList))
    if result:
        ok_urlList.append(f"{result[0]}")
        # まだチェックしていないURLを指定する
        searchLink(result[0])
    else:
   # チェックするURLがなくなったら処理を終了する
        return

searchLink("")
print(ok_urlList)