Node.jsで開発環境をセットアップする(ESLint、Prettier、husky)

Node.jsの開発環境で、以下の機能をセットアップする方法について解説します。

  • ESLintによるlint
  • Prettierによるコードフォーマット
  • huskyによるpre-commit git hooks

開発環境を作成する

最初に構築対象となる開発環境を用意します。

$ npm init -y
Wrote to /Users/t0k0sh1/workspace/node_lint/package.json:

{
  "name": "node_lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

今回の説明とは関係ないため、デフォルトのままで進めます。

パッケージをインストールする

次に必要なパッケージをインストールします。

  • eslint – lintツール
  • prettier – formatterツール
  • eslint-config-prettier – eslintのフォーマットがprettierと競合する場合はeslint側をOFFにする
  • husky – pre-commit git hooksツール
$ npm i -D eslint eslint-config-prettier prettier husky

added 85 packages, and audited 86 packages in 4s

15 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

eslint-plugin-prettierは非推奨になっているため、eslint-config-prettierを使用しています。

インストール後のpackage.jsonは以下のようになっています。

// package.json
{
  "name": "node_lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "eslint": "^8.13.0",
    "eslint-config-prettier": "^8.5.0",
    "husky": "^7.0.4",
    "prettier": "^2.6.2"
  }
}

lintの設定を行う

まずはESLintの設定を行います。ここでは必要最低限の設定にしています。prettiereslint:recommendの後に書いてください。

// .eslintrc.json
{
  "extends": [
    "eslint:recommended",
    "prettier"
  ]
}

次にeslintでのlint(修正あり)とprettierでのフォーマットを行うスクリプトを追加します。

// package.json
{
  "name": "node_lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint --cache --fix . && prettier --write ."
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "eslint": "^8.13.0",
    "eslint-config-prettier": "^8.5.0",
    "husky": "^7.0.4",
    "prettier": "^2.6.2"
  }
}

コードを作成して動作確認を行う

実際のコードを作成して動作確認をしておきましょう。

// index.js
function hello(name) {
  return 'Hello, ' + name;
}

console.log(hello("John"));

簡単なプログラムを書きました。動作確認ができるようにlintでエラーになるコードになっています。

では動作確認します。

$ node index.js
Hello, John

エラーにならず動作していることを確認できました。

では、このコードに対してlintprettierを実行します。

$ npm run lint

> node_lint@1.0.0 lint
> eslint --cache --fix . && prettier --write .


/Users/t0k0sh1/workspace/node_lint/index.js
  6:1  error  'console' is not defined  no-undef

✖ 1 problem (1 error, 0 warnings)

consoleが未定義であるとエラーになりました。これは後で修正(除去)しますが、今はこのままにしておきます。

ここまでに作成したコードをコミットしておきます。

git initコマンドでgitリポジトリを初期化します。

$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:   git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint:   git branch -m <name>
Initialized empty Git repository in /Users/t0k0sh1/workspace/node_lint/.git/

コミットするにあたり、node_modules.eslintcacheは対象から外したいので、.gitignoreを作成しておきます。

// .gitignore
node_modules/
.eslintcache

作成したファイルをステージングエリアに追加します。

$ git add .

ローカルリポジトリにコミットしておきます。

$ git commit -m 'first commit'
[master (root-commit) ec47452] first commit
 5 files changed, 1682 insertions(+)
 create mode 100644 .eslintrc.json
 create mode 100644 .gitignore
 create mode 100644 index.js
 create mode 100644 package-lock.json
 create mode 100644 package.json

pre-commit git hooksを設定する

huskyを使ってpre-commit git hooksを設定していきます。手順は公式の手順(https://github.com/typicode/husky)に従いますが、pre-commitに追加するコマンドがnpm run lintになっている点にだけ注意してください(公式の手順ではnpm test

$ npm set-script prepare "husky install"
$ npm run prepare

> node_lint@1.0.0 prepare
> husky install

husky - Git hooks installed
$ npx husky add .husky/pre-commit "npm run lint"
husky - created .husky/pre-commit
$ git add .husky/pre-commit

手順どおりに進めると、package.jsonがステージングエリアに追加されていないため、これを追加してローカルリポジトリにコミットします。

$ git add package.json
$ git commit -m 'setup husky'

> node_lint@1.0.0 lint
> prettier --write '**/*.js' && eslint .

index.js 25ms

/Users/t0k0sh1/workspace/node_lint/index.js
  6:1  error  'console' is not defined  no-undef

✖ 1 problem (1 error, 0 warnings)

husky - pre-commit hook exited with code 1 (error)

先ほどコマンド実行で確認した時と同じようにconsoleが未定義であるとエラーになりました。

pre-commitが機能していることを確認できましたが、念のためコミットされていないかを確認しておきます。

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   .husky/pre-commit
        modified:   package.json

コミットが行われておらず、pre-commitが正しく動作していることを確認できました。

コードを修正して再度コミットする

コードを修正して再度コミットしましょう。

ここでは単純にconsole.log関数を削除しておきます。

// index.js
function hello(name) {
  return "Hello, " + name;
}

hello("John");

念のため、コマンドで確認しておきます。

$ npm run lint

> node_lint@1.0.0 lint
> eslint --cache --fix . && prettier --write .

.eslintrc.json 20ms
index.js 7ms
package-lock.json 30ms
package.json 6ms

エラーにならないことを確認できましたので、ステージングエリアに追加します。

$ git add index.js

では、再度コミットしましょう。

$ git commit -m 'setup husky'
> node_lint@1.0.0 lint
> prettier --write '**/*.js' && eslint .

index.js 25ms
[master ed5dcae] setup husky
 3 files changed, 7 insertions(+), 2 deletions(-)
 create mode 100755 .husky/pre-commit

今度はエラーが表示されず、コミットが実行されました。

git statusgit logでもコミットされていることを確認します。

git status
On branch master
nothing to commit, working tree clean
$ git log
commit ed5dcae1e5e2487854e78e0d388a2f503324231d (HEAD -> master)
Author: Takashi Yamashina <takashi.yamashina@gmail.com>
Date:   Sat Apr 9 12:51:04 2022 +0900

    setup husky

commit 15186ea9211ed9d5ee4de9aa7dab7e2e04bac9de
Author: Takashi Yamashina <takashi.yamashina@gmail.com>
Date:   Sat Apr 9 12:37:45 2022 +0900

    first commit

問題なくコミットできていることが確認できました。

Prettierで特定のファイル、フォルダを対象から除外する(.prettierignore)

Prettierで特定のファイルやフォルダを対象から除外する方法について解説します。

.prettierignoreを作成する

.prettierignoreを作成し、そこに対象から除外したいファイルやフォルダを書きます。

config.js
test/

上記の例では、config.jsというファイルと、testフォルダを対象から除外しています。

実際に試してみる

実際にどう動くのか確認しましょう。

package.json

package.jsonは次のとおりです。prettierをインストールし、prettierコマンドを実行するスクリプトを追加しています。

{
  "name": "node_prettier",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "format": "prettier --write '**/*.js'"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "prettier": "^2.6.2"
  }
}

Prettierは設定ファイルがなくても実行可能なため、ここでは設定ファイルは作成せずに進めます。

プロジェクトの構造

プロジェクトの構造は以下のようになっており、用意した4つのJSファイルはすべてフォーマットされる書き方になっています。

$ tree
.
├── file1.js
├── file2.js
├── dir3
│   └── file3.js
├── dir4
│   └── file4.js
├── package-lock.json
└── package.json

各ファイルの中身は若干の違いはありますが、以下のような内容になっています。

function file1() {
console.log('file1');
}

関数内のインデントがない点と、文字列の囲み文字がダブルクォーテーションではなくシングルクォーテーションになっている点がフォーマットの対象となる箇所です。

Prettierを実行する

最初に.prettierignoreを用意せずに実行します。

$ npm run format

> node_prettier@1.0.0 format
> prettier --write '**/*.js'

dir3/file3.js 22ms
dir4/file4.js 2ms
file1.js 2ms
file2.js 1ms

プロジェクト直下の2ファイル、2つのフォルダ内のそれぞれのファイルが対象になっていることがわかります。

function file1() {
  console.log("file1");
}

実際にファイルを見てみると、インデントおよび文字列の囲み文字が修正されていることが確認できました。

次に.prettierignoreを作成し、プロジェクト直下の1ファイルと2つのファイルのうちの1つを除外してから再度実行してみましょう。

file2.js
dir4/

先ほどと同様に実行してみると、除外したファイルとフォルダ内のファイルが表示されなくなりました。

$ npm run format

> node_prettier@1.0.0 format
> prettier --write '**/*.js'

dir3/file3.js 23ms
file1.js 2ms

ファイルも確認してみると、

ファイル名が表示されたファイルは、先ほどと同様にフォーマットされていますが、

function file1() {
  console.log("file1");
}

ファイル名が表示されなかった(除外された)ファイルはフォーマットされていません。

function file2() {
console.log('file2');
}

node_modulesの除外は不要

実行してみるとわかりますが、node_modulesの除外は不要です。

そのため、対象としたいフォルダ内に除外したいファイルやフォルダがない場合は.prettierignoreを作成する必要はありません。

ESLintで特定のファイル、フォルダを対象から除外する(.eslintignore)

ESLintで特定のファイルやフォルダを対象から除外する方法について解説します。

.eslintignoreを作成する

.eslintignoreを作成し、そこに対象から除外したいファイルやフォルダを書きます。

config.js
test/

上記の例では、config.jsというファイルと、testフォルダを対象から除外しています。

実際に試してみる

実際にどう動くのか確認しましょう。

package.json

package.jsonは次のとおりです。eslintをインストールし、eslintコマンドを実行するスクリプトを追加しています。

{
  "name": "node_eslint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint ./"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "eslint": "^8.12.0"
  }
}

.eslintrc.json

.eslintrc.jsonは必要最小限にしています。

{
    "extends": [
      "eslint:recommended"
    ]
}

プロジェクトの構造

プロジェクトの構造は以下のようになっており、用意した4つのJSファイルはすべてLintでエラーが検出されます。

$ tree
.
├── file1.js
├── file2.js
├── dir3
│   └── file3.js
├── dir4
│   └── file4.js
├── .eslintrc.json
├── package-lock.json
└── package.json

ESLintを実行する

最初に.eslintignoreを用意せずに実行してみます。

$ npm run lint                                                                                                                     

> node_eslint@1.0.0 lint
> eslint ./


/Users/t0k0sh1/workspace/node_eslint/dir3/file3.js
  1:10  error  'file3' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/dir4/file4.js
  1:10  error  'file4' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/file1.js
  1:10  error  'file1' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/file2.js
  1:10  error  'file2' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

✖ 8 problems (8 errors, 0 warnings)

プロジェクト直下の2ファイルもフォルダ内の2ファイルもエラーになっていることがわかります。

次に.eslintignoreを作成し、プロジェクト直下の1ファイルと2つのファイルのうちの1つを除外してから再度実行してみましょう。

file2.js
dir4/

先ほどと同様に実行してみると、除外したファイルとフォルダ内のファイルがエラーに表示されなくなりました。

$ npm run lint

> node_eslint@1.0.0 lint
> eslint ./


/Users/t0k0sh1/workspace/node_eslint/dir3/file3.js
  1:10  error  'file3' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/file1.js
  1:10  error  'file1' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

✖ 4 problems (4 errors, 0 warnings)

node_modulesの除外は不要

実行してみるとわかりますが、node_modulesの除外は不要です。

そのため、対象としたいフォルダ内に除外したいファイルやフォルダがない場合は.eslintignoreを作成する必要はありません。

Angularでキャッシュ対策を行う(–output-hashing=all)

Angularでキャッシュが使用されてアプリケーションが更新されないことがある問題を解消する方法について解説します。

この方法はプログラムを変更することなく実施できますが、必ずしも安全であるとは限りません。
実際にこの方法を試して、うまくいかない(キャッシュ対策ができない、というのではなく動作しない)ことがありました。このことから、アプリケーションの使用しているAngularのバージョンやアプリケーションの特性等の要因によって副作用が発生する場合があります。

必ずテスト環境で十分確認してから本番環境へ適用してください

ビルドされたファイルにハッシュを付与する

ng buildコマンドのオプションに--output-hashing=allを設定します。

$ ng build --configuration production --output-hashing=all

もし、ビルドオプションを変更できない場合は、angular.jsonoutputHashingオプションを設定します。

   "configurations": {
     "production": {
       ・・・
       "outputHashing": "all"
       ・・・
     },
   },

Apple Silicon macOSでディープラーニングの環境を構築する(Miniforge使用、Tensorflow、Tensorflow addons導入)

Apple Silicon(M1、M1Max) macOSでディープラーニングの環境を構築する方法について解説します。

本手順は2022/4/1時点のものです。現状ではHomebrewやPyenv等でインストールしたPythonではTensorflowを導入できないようです。この状況も今後変わってくる可能性があります。

Miniforgeのインストール

現状ではMiniforgeを使うのが最も楽な手順のようです。

MiniforgeのGithubサイトからApple Silicon用のインストーラをダウンロードしてください。ファイル名はMiniforge3-MacOSX-arm64.shとなっています。

迷うところはありませんが、念のため応答する箇所について掲載しておきます。

$ bash ~/Downloads/Miniforge3-MacOSX-arm64.sh                                                                                                                                                           

Welcome to Miniforge3 4.12.0-0

In order to continue the installation process, please review the license
agreement.
Please, press ENTER to continue
>>> ← ENTERを押下
Miniforge installer code uses BSD-3-Clause license as stated below.

 ・・・

Do you accept the license terms? [yes|no]
[no] >>> yes ← yesを入力してENTERを押下

Miniforge3 will now be installed into this location:
/Users/t0k0sh1/miniforge3

  - Press ENTER to confirm the location
  - Press CTRL-C to abort the installation
  - Or specify a different location below

[/Users/t0k0sh1/miniforge3] >>> ← ENTERを押下
PREFIX=/Users/t0k0sh1/miniforge3
Unpacking payload ...

 ・・・

Do you wish the installer to initialize Miniforge3
by running conda init? [yes|no]
[no] >>> no ← noを入力してENTERを押下

You have chosen to not have conda modify your shell scripts at all.
To activate conda's base environment in your current shell session:

eval "$(/Users/t0k0sh1/miniforge3/bin/conda shell.YOUR_SHELL_NAME hook)"

To install conda's shell functions for easier access, first activate, then:

conda init

If you'd prefer that conda's base environment not be activated on startup,
   set the auto_activate_base parameter to false:

conda config --set auto_activate_base false

Thank you for installing Miniforge3!

インストールが完了すると、以下のようなメッセージが表示されています。このうち、YOUR_SHELL_NAME部分を書き換えてシェルの設定ファイルに追記します。

eval "$(/Users/t0k0sh1/miniforge3/bin/conda shell.YOUR_SHELL_NAME hook)"

macOSのデフォルトシェルはzshですので、以下のように書き換えて、.zshrcに追記してください。

eval "$(/Users/t0k0sh1/miniforge3/bin/conda shell.zsh hook)"

conda環境の設定を行う

次にconda環境の設定を行います。

conda環境の自動有効化をOFFにする

前述の作業が完了し、再度シェルにログインすると、自動でconda環境が有効になります。これでも問題ない方は以下の設定変更を行う必要はありませんが、そうでない方はconda環境が自動で有効にならないように設定変更をしてください。

$ conda config --set auto_activate_base false

一度conda環境を無効化しておきます。

$ conda deactivate

Tensorflowをインストールする環境を作成する

Tensorflowをインストールする環境を作成しましょう。

執筆時点では、TensorFlow 2.8.0が最新なため、これをインストールします。Tensorflow 2.8.0ではPython 3.9に対応しているため、これを使用します。

$ conda create --name tensorflow28 python=3.9

環境名はなんでも構いませんが、ここではtensorflow28としています。

$ conda create --name tensorflow28 python=3.9                                                                                                                                                           
Collecting package metadata (current_repodata.json): done
Solving environment: done

 ・・・

#
# To activate this environment, use
#
#     $ conda activate tensorflow28
#
# To deactivate an active environment, use
#
#     $ conda deactivate

作成した環境を有効化します。

$ conda activate tensorflow28

以下の手順は作成した環境が有効化されていることを前提に進めます。

Tensorflowをインストールする

作成・有効化した環境にTensorflowをインストールします。

Numpy、OpenCV、Matplotlibをインストールする

Tensorflowをインストールする前にNumpy、OpenCV、Matplotlibをインストールしておいた方がよいようなので、先にconda installコマンドでインストールします。

$ conda install numpy opencv matplotlib

Tensorflowをインストールする

Tensorflowを以下の順番でインストールします。

$ conda install -c apple tensorflow-deps
$ python -m pip install tensorflow-macos
$ python -m pip install tensorflow-metal

Tensorflow addonsのビルド・インストールする

Tensorflowの拡張ライブラリであるTensorflow addonsを使用するためには、Apple Siliconではソースコードからビルドしてインストール必要があります(conda installpip installではうまくいかない)。

ビルドにはbazelが必要なため、まずはこれをインストールします。

$ conda install bazel

次にwheelsetuptoolsが最新でないとビルドに失敗するという報告もあるため、最新化しておきます。

$ python -m pip install --upgrade wheel setuptools

準備が整いましたので、Tensorflow addonsをダウンロードし、ビルドします。

$ git clone https://github.com/tensorflow/addons.git
$ cd addons
$ python ./configure.py
$ bazel build build_pip_pkg
$ bazel-bin/build_pip_pkg artifacts

ビルドが完了すると、artifactsディレクトリの下にファイルが作成されます。

$ ls ./artifacts                                                                                                                                                                                          
tensorflow_addons-0.17.0.dev0-cp39-cp39-macosx_11_0_arm64.whl

作成されていることが確認できましたら、これをインストールします。

$ python -m pip install ./artifacts/tensorflow_addons-0.17.0.dev0-cp39-cp39-macosx_11_0_arm64.whl

これで、TensorflowおよびTensorflow addonsのインストールが完了となります。

参考文献

本手順は以下を参考に作成しました。

[Java]ラムダ式を使ってコレクションを操作する(Java Lambda)

Java 8から導入されたラムダ式を使ってコレクションを操作する方法について解説します。ここでは、実用的なものに的を絞って解説しています。

前提(説明時に使用しているDTO)

説明で使用しているDTOを提示しておきます。ここではUserDtoクラスを使用しています。

import java.io.Serializable;

public class UserDto implements Serializable {
    private String userId;
    private String userName;
    private int age;

    // Setter, Getterは省略

    public String toString() {
        return "userId=" + userId + ",userName=" + userName + ",age=" + age;
    }

}

ListをMapに変換する

ListをMapに変換する方法について解説します。

以下の例では、UserDtoクラスを要素に持つListをMapに変換しています。MapのキーはUserDtoのuserId、値はUserDtoにしています。

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        // UserDtoを要素として持つListを用意する
        List<UserDto> list = new ArrayList<>();
        UserDto user1 = new UserDto();
        user1.setUserId("001");
        user1.setUserName("John Smith");
        user1.setAge(20);
        list.add(user1);
        UserDto user2 = new UserDto();
        user2.setUserId("002");
        user2.setUserName("Maria Cambell");
        user2.setAge(28);
        list.add(user2);
        
        // ListをMapに変換する(キーはUserDTOのUserIdを使用する)
        Map<String, UserDto> map = list.stream().collect(Collectors.toMap(s -> s.getUserId(), s -> s));

        // Listの出力結果を確認する
        System.out.println(list);
        // [userId=001,userName=John Smith,age=20,userId=002, userName=Maria Cambell,age=28]
        
        // 変換後のMapの出力結果を確認する
        System.out.println(map);
        // {001=userId=001,userName=John Smith,age=20, 002=userId=002,userName=Maria Cambell,age=28}
    }
}

Listから一部の項目を抽出する

Listから一部の項目を抽出する方法について解説します。

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        // UserDtoを要素として持つListを用意する
        List<UserDto> list = new ArrayList<>();
        UserDto user1 = new UserDto();
        user1.setUserId("001");
        user1.setUserName("John Smith");
        user1.setAge(20);
        list.add(user1);
        UserDto user2 = new UserDto();
        user2.setUserId("002");
        user2.setUserName("Maria Cambell");
        user2.setAge(28);
        list.add(user2);

        // Listから項目の一部(ここではUserDtoのuserIdを抽出)し、リストにする
        List<String> userIds = list.stream().map(s -> s.getUserId()).collect(Collectors.toList());
  
        
        // 抽出前のListの内容を確認する
        System.out.println(list);
        // [userId=001,userName=John Smith,age=20, userId=002,userName=Maria Cambell,age=28]
        // 抽出した項目を確認する
        System.out.println(userIds);
        // [001, 002]
    }
}

[Oracle]SQL*Plusで実行計画を取得する(SET AUTOTRACE)

実行計画の確認にはいくつかを方法がありますが、ここでは最も手軽に実施できるSQL*Plusでの実行計画の取得の方法について解説します。

実行計画を取得するための設定を行う

私がよく使っている設定をご紹介します。とりあえずこの設定を使っていただければ実行計画を取得できるようになります。

SQL> set lines 1000
SQL> set pages 1000
SQL> set timing on
SQL> set autotrace traceonly

以上です。

この設定を行った状態でSQLを実行すると以下のようになります。

SQL> select * from dept
  2  /

レコードが選択されませんでした。

経過: 00:00:00.02

実行計画
----------------------------------------------------------
Plan hash value: 3383998547

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    30 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| DEPT |     1 |    30 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------


統計
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          0  consistent gets
          0  physical reads
          0  redo size
        449  bytes sent via SQL*Net to client
        369  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL>

実行計画と統計情報が出力されていることがわかります。

上記手順を実施しているときにエラーが出る場合があります。その場合は後述の「手順の実施時にエラーが出る場合」を参照してください。

これだけだとよくわからないと思いますので、各コマンドについて解説します。

表示幅を広げる

linesizeで結果行の幅を設定します。実行計画の出力内容は表を含むため、途中で折り返されるとかなり見にくいです。そこでとりあえず折り返されないような大きな値を設定しています。

正式にはlinesizeですが、linesでも設定できます。タイプ数が少ない方が楽なのでlinesを使用しています。

set lines 1000

1ページの行数を増やす

実行計画の出力の途中で改ページすると見づらいため、1ページの行数も増やしています。こちらもとりあえずで増やしていますが、普通に使う分には問題は起こらないと思いますが、あまりにも長いSQLの実行計画を確認するときに、改ページされるようであれば値を増やしてください。

先ほどと同様にpagesizeが正式な設定ですが、pagesでも設定できます。

set pages 1000

タイミング統計をONにする

タイミング統計をONにすることで、実行時間を10ミリ秒単位で出力できるようになります。

経過: 00:00:00.02

実行時間もチューニングの重要な情報源となりますし、出力結果が邪魔になることもないので、ONにしておきましょう。

set timing on

実行計画の設定を行う

実行計画の出力設定は、autotraceで行います。出力できる内容は、

  • 実行結果
  • 実行計画
  • 統計情報

の3つがありますが、実行結果を必要としない場合はtraceonlyを設定し、実行結果を確認したい場合はoffに切り替えるのがおすすめです。

設定説明
set autotrace on explain実行結果と実行計画を表示する
set autotrace on statistics実行結果と統計情報を表示し、実行計画をは表示しない
set autotrace on実行結果、実行計画、統計情報を表示する
set autotrace traceonly実行計画と統計情報を表示し、実行結果は表示しない
set autotrace offautotraceの設定をOFFにする
set autotrace traceonly

手順の実行時にエラーが出る場合

これから説明する手順を実行したときに、以下のようなエラーが出る場合があります。

SQL> set autotrace traceonly
SP2-0618: セッション識別子が見つかりません。PLUSTRACEロールが有効かを確認してください。
SP2-0611: STATISTICSレポートを使用可能にするときにエラーが発生しました。

このエラーが出る場合は、以下の手順を実行してPLUSTRACEロールを有効にしてください。

SYSDBAでCDBにログインする

ロールを追加するため、SYSDBACDBにログインします。

ORACLE_SID=ORCLCDB sqlplus / as sysdba

SQL*Plus: Release 19.0.0.0.0 - Production on Tue Mar 29 15:44:05 2022
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.


Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL> 

PDBに切り替える

ロールを作成するPDBに切り替えます。ここではpdb1というPDBに切り替えています。

SQL> alter session set container=pdb1;

Session altered.

SQL> 

ロールを作成するPDBというのは、実行計画を取得するPDBのことです。

PLUSTRACEロールを作成するSQLファイルを実行する

OracleDatabaseに用意されているSQLファイルを実行します。SQLファイルのファイル名はplustrace.sqlではなくplustrce.sqlですので、打ち間違いに注意してください(aがない)。

SQL> @?/sqlplus/admin/plustrce.sql
SQL> 
SQL> drop role plustrace;
drop role plustrace
          *
ERROR at line 1:
ORA-01919: role 'PLUSTRACE' does not exist


SQL> create role plustrace;

Role created.

SQL> 
SQL> grant select on v_$sesstat to plustrace;

Grant succeeded.

SQL> grant select on v_$statname to plustrace;

Grant succeeded.

SQL> grant select on v_$mystat to plustrace;

Grant succeeded.

SQL> grant plustrace to dba with admin option;

Grant succeeded.

SQL> 
SQL> set echo off

PLUSTRACEロールをユーザーに付与する

作成したPLUSTRACEロールをユーザーに付与します。ここでは、user01ユーザーに設定しています。

SQL> grant plustrace to user01;

Grant succeeded.

SQL>
SQL> exit
Disconnected from Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

再度、該当ユーザーでログインしてautotraceの設定を行ってください。

[Python]VSCodeでPythonのフォーマッタを設定する(black等のライブラリをPrettierプラグインと共存させる)

Visual Studio Codeで、Python以外はPrettierプラグインを使用し、PythonはPrettierプラグインを使わずにblackを使用する方法について解説します。

共通の設定

Prettierプラグインを有効化するにはeditor.defaultFormatterを設定します。保存時にフォーマットしたいので、editor.formatOnSaveも設定します。

{

  ・・・
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  ・・・
 }

Pythonの設定

次にPythonの設定です。PythonではPrettierプラグインを使用せずにblackを使用します。ただし、editor.defaultFormatterの設定が有効な場合、editor.defaultFormatterの方が優先されてしまうため、Pythonではこの設定を無効化(null)します。

以下は、blackを使用するように設定している例です。ここではblackを使用していますが、他のライブラリでも構いません。

{
  ・・・
  "[python]": {
    "editor.defaultFormatter": null
  },
  "python.formatting.provider": "black",
  ・・・

}

上記設定により、PythonだけはPrettierプラグインを使用せず、black等のライブラリを使用できます。

外部キー制約でのON DELETE CASCADEオプションの使い方

外部キー制約でのON DELETE CASCADEオプションの使い方を具体的な例を使って解説します。

ON DELETE CASCADEオプションは親テーブル(参照先のテーブル)を削除するときに子テーブル(参照元のテーブル)も一緒に削除するために使用するオプションになります。

解説に使用するデータベースとテーブルについて

解説に使用するデータベースとテーブルについて説明します。

確認にはOracle Database 19cを使用しています。若干の構文の違いはありますが、他の主要なデータベースでも同様の操作が可能です。

まずは親テーブルを作成します。親テーブルのMAINテーブルは主キーIDのみを持ったテーブルです。

CREATE TABLE MAIN
(
  ID NUMBER(3),
  CONSTRAINT MAIN_PK PRIMARY KEY (ID)
)

次に子テーブルを作成します。子テーブルのSUBテーブルはIDとSEQを主キーに持つテーブルです。

CREATE TABLE SUB
(
  ID  NUMBER(3),
  SEQ NUMBER(2),
  CONSTRAINT SUB_PK PRIMARY KEY (ID, SEQ)
)

ON DELETE CASCADE オプションなしの場合

では、ON DELETE CASCADEオプションを使用しない場合の動作を見てみましょう。

SUBテーブルのIDに外部キー制約を設定します。

SQL> ALTER TABLE SUB ADD CONSTRAINT SUB_ID_FK
   2 FOREIGN KEY (ID) REFERENCES MAIN(ID)

表が変更されました。

では、データを投入して削除時の動作を確認します。

SQL> INSERT INTO MAIN VALUES (1)

1行が作成されました。
SQL> INSERT INTO SUB VALUES (1, 1)

1行が作成されました。
SQL> INSERT INTO SUB VALUES (1, 2)

1行が作成されました。

削除する前にデータを確認しておきましょう。

SQL> SELECT * FROM MAIN

ID
--
 1

1行が選択されました。
SQL> SELECT * FROM SUB

ID SEQ
-- ---
 1   1
 1   2

2行が選択されました。

MAINテーブルのID=1のレコードにSUBテーブルのID=1のレコードが2件ぶら下がっている形になっています。

データが投入できましたので、親テーブルMAINテーブルのレコードを削除してみます。

SQL> DELETE FROM MAIN WHERE ID = 1
DELETE FROM MAIN WHERE ID = 1
*
行1でエラーが発生しました。:
ORA-02292: 整合性制約(USER01.SUB_ID_FK)に違反しました - 子レコードがあります

整合性制約(外部キー制約)で削除に失敗しました。ON DELETE CASCADEオプションがない場合は、子テーブルを削除してからでないと親テーブルを削除できません。

念のためデータが削除されていないことを確認しておきます。

SQL> SELECT * FROM MAIN

ID
--
 1

1行が選択されました。
SQL> SELECT * FROM SUB

ID SEQ
-- ---
 1   1
 1   2

2行が選択されました。

ON DELETE CASCADEオプションありの場合

では、ON DELETE CASCADEオプションをつけた場合の削除の動きを確認します。

すでに設定済みの外部キーを削除しておきます。

SQL> ALTER TABLE SUB DROP CONSTRAINT  SUB_ID_FK

表が変更されました。

今度はON DELETE CASCADEオプション付きで外部キー制約を設定します。

SQL> ALTER TABLE SUB ADD CONSTRAINT SUB_ID_FK
   2 FOREIGN KEY (ID) REFERENCES MAIN(ID)
   3 ON DELETE CASCADE

表が変更されました。

では、もう一度削除を試します。

> DELETE FROM MAIN WHERE ID = 1

1行が削除されました。

今度は削除できました。

削除後のデータを確認します。

SQL> SELECT * FROM MAIN

レコードが選択されませんでした。
SQL> SELECT * FROM SUB

レコードが選択されませんでした。

削除したMAINテーブルのレコードだけでなく、SUBテーブルのID=1のレコードも削除されています。

どういった場合にON DELETE CASCADEオプションを使うか

まず、外部キー制約を使用する場合に限られているということが前提となります。

上記の前提の上で、以下の2つのどちらの効果を狙いたいかで決めるとよいでしょう。

  • 子テーブルにデータがあるときに親テーブルが削除されるのを防ぎたい
  • 親テーブルが削除されるときに子テーブルをまとめて削除したい

もう少し具体的な例で説明します。

ON DELETE CASCADEオプションを使いたくないケース

ON DELETE CASCADEオプションを使いたくないケース、使うべきでないケースとして挙げられるのが、マスタテーブルに対して外部キー制約を設定しているケースです。

例えば、注文テーブルにある顧客番号に対して、顧客マスタの顧客番号への外部キーを設定している場合、ON DELETE CASCADEオプションを設定していると、注文テーブルのレコードの削除によって顧客マスタのレコードが削除される場合があります。
これは望ましい挙動でないことから、マスタテーブルへの外部キー設定の場合はON DELETE CASCADEオプションを使用するべきではありません。

ON DELETE CASCADE オプションを使いたいケース

ON DELETE CASCADEオプションを使いたいケースとして挙げられるのが、子テーブルが親テーブルに対して外部キー制約を設定しているケースです。

例えば、注文テーブルと注文明細テーブルがあり、注文明細の注文番号に対して、注文テーブルの注文番号への外部キーを設定している場合は、ON DELETE CASCADEオプションを設定していると、注文テーブルのレコード削除時に注文明細テーブルのレコードをまとめて削除してくれるため便利です。
ON DELETE CASCADEオプションを設定していない場合は、先に注文明細テーブルの該当する注文番号のレコードを先に削除してからでないと注文テーブルのレコードを削除できないため、ON DELETE CASCADEオプションを使うことが削除を行うプログラムの構造をすっきりとさせることができる効果があります。

まとめ

前述の例をまとめると以下のようになります。

CREATE TABLE 注文
(
  注文番号 NUMBER(10),
  顧客番号 NUMBER(3),
  CONSTRAINT 注文_PK PRIMARY KEY (ID)
)

CREATE TABLE 注文明細
(
  注文番号 NUMBER(10),
  注文明細連番 NUMBER(10),
  CONSTRAINT 注文明細_PK PRIMARY KEY (注文番号, 注文明細番号)
)

CREATE TABLE 顧客マスタ
(
  顧客番号 NUMBER(3),
  CONSTRAINT 顧客マスタ_PK PRIMARY KEY (顧客番号)

-- 注文から顧客マスタへの外部キーではON DELETE CASCADEオプションをつけない
ALTER TABLE SUB ADD CONSTRAINT 注文_顧客番号_FK
FOREIGN KEY (顧客番号) REFERENCES 顧客マスタ(顧客番号)

-- 注文明細から注文への外部キーではON DELETE CASCADEオプションをつける
ALTER TABLE SUB ADD CONSTRAINT 注文明細_注文番号_FK
FOREIGN KEY (注文番号) REFERENCES 注文(注文番号)
ON DELETE CASCADE

このようにON DELETE CASCADEオプションを適切に設定する/しないことでデータを安全に、効率よく管理できるようになります。

[Git]ファイルをステージングエリアに追加する(git add)

Gitでファイルをステージングエリア(インデックス)に追加する方法を解説します。

ファイル名を指定して追加する

一番オーソドックスな方法は、追加したいファイルをgit addコマンドで指定する方法です。

$ git add test.txt

ファイルをまとめて追加する

変更をまとめて追加したい場合は、ファイル名の代わりに.を指定するとまとめて追加できます。

$ git add .

-Aオプションを使っても同様のことができます。

$ git add -A

ワイルドカードを指定して追加する

特定の拡張子のファイルや特定のフォルダ(ディレクトリ)にあるファイルをまとめて追加したい場合は、ワイルドカードを使うと便利です。

$ git add *.java
$ git add src/*

Gitで管理しているファイルだけをまとめて追加する

git add .の場合、Gitで管理しているファイルも管理していないファイルもまとめて追加されます。Gitで管理しているファイルだけを追加したい場合は-uオプションを使います。

$ git add -u

[NumPy]正規化(Normalization)を行う

正規化とはデータを扱いやすいスケールに変更する手法です。ここでは代表的なMin-Max normalization、Z-score normalizationについて解説します。

  • Min-Max normalization
  • Z-score normalization

Min-Max normalization

正規化というとMin-Max normalizationを指すといわれることもありますが、正直そこまで気にする必要はありません。どちらかというと、正規化を行うときにデータの性質に応じて適切な手法を選択できる方が重要です。

Min-Max normalizationとは

Min-Max normalizationとは、データを最小値0、最大値1にスケールする手法です。

データ$x_i$をMin-Max normalizationで正規化した$x^{\prime}_i$は以下の式で求めます。

$$ x^{\prime}_i = \frac{x_i – min(x)}{max(x) – min(x)} \quad (i = 1, 2, \cdots , n) $$

Min-Max normalizationは外れ値に弱いという特徴があります。最小値と最大値がはっきりしている場合に適した手法です。外れ値が含まれる場合は後述のZ-score normalizationを使用する方が適しています。

NumPyによる実装

では、実際にNumPyで実装してみましょう。

まずは、正規化前の5×5の行列を作成します。再現しやすいように乱数のシードは固定しておきます。

np.random.seed(123)
x = np.random.random((5, 5))
print(x)
[[0.69646919 0.28613933 0.22685145 0.55131477 0.71946897]
 [0.42310646 0.9807642  0.68482974 0.4809319  0.39211752]
 [0.34317802 0.72904971 0.43857224 0.0596779  0.39804426]
 [0.73799541 0.18249173 0.17545176 0.53155137 0.53182759]
 [0.63440096 0.84943179 0.72445532 0.61102351 0.72244338]]

正規化前後でどう変わったかわかりやすいように最小値、最大値、平均値、標準偏差を求めておきます。

print('最小値 :%f' % np.min(x))
print('最大値 :%f' % np.max(x))
print('平均値 :%f' % np.mean(x))
print('標準偏差:%f' % np.std(x))
最小値 :0.059678
最大値 :0.980764
平均値 :0.524464
標準偏差:0.226296

式の定義どおりに実装します。np.min関数で最小値、np.max関数で最大値を求めています。

x_norm = (x - np.min(x)) / (np.max(x) - np.min(x))
print(x_norm)
[[0.02615693 0.72523256 0.32482616 0.41610292 0.11393134]
 [[0.69134813 0.24586343 0.18149608 0.53375766 0.71631841]
 [0.39456516 1.         0.67871147 0.45734477 0.36092125]
 [0.30778888 0.72671997 0.41135597 0.         0.36735576]
 [0.73643209 0.13333586 0.12569274 0.51230105 0.51260093]
 [0.62396223 0.85741574 0.72173197 0.59858193 0.71954765]]

ぱっと見で最小値0、最大値1であることはわかりますが、念のため先ほどと同じように最小値、最大値、平均値、標準偏差を確認しましょう。

print('最小値 :%f' % np.min(x_norm))
print('最大値 :%f' % np.max(x_norm))
print('平均値 :%f' % np.mean(x_norm))
print('標準偏差:%f' % np.std(x_norm))
最小値 :0.000000
最大値 :1.000000
平均値 :0.504606
標準偏差:0.245684

最小値0、最大値1であることが確認できます。加えて、若干値が違いますが、平均値と標準偏差はほとんど変わっていないことがわかります。

最小値0、最大値1にスケールする仕組みを理解する

Z-score normalizationは平均0、標準偏差1にスケールする手法になります。

わかりやすいデータを使って計算過程をひとつずつ確認して、最小値0、最大値1の範囲にスケールする仕組みを確認していきましょう。

まずは、以下のようなデータを用意します。10、20、・・・、50と10ずつ増える5個のデータです。

x = np.array([10, 20, 30, 40, 50])
print(x)
[10 20 30 40 50]

まずは分子から確認します。データから最小値を引きます。最小値は10なので、各要素から引くと以下のようになります。

print(x - np.min(x))
[ 0 10 20 30 40]

次に分母を見てみます。最大値50から最小値10を引くので、40になります。

print(np.max(x) - np.min(x))
40

すでに計算した分子の最大値は40で、分母と同じ値ですので、割り算の結果は1になります。一方で、最小値は0ですので、割り算の結果は0になります。それ以外の値は最小値と最大値の間に収まるため、計算結果はすべて0以上1以下になります。

print((x - np.min(x)) / (np.max(x) - np.min(x)))
[0.   0.25 0.5  0.75 1.  ]

Z-score normalization

次にZ-score normalizationについて確認します。標準化(Standardization)とも呼ばれることもありますが、こちらもそれほど気にする必要はありません。

Z-score normalizationとは

データ$x_i$をZ-score normalizationで正規化した$x^{\prime}_i$は以下の式で求めます。

$$ x^{\prime}_i = \frac{x_i – \bar{x}}{\sigma} \quad (i = 1, 2, \cdots , n) $$

NumPyによる実装

正規化前のデータおよび最小値、最大値、平均値、標準偏差を再掲しておきます。

np.random.seed(123)
x = np.random.random((5, 5))
print(x)
[[0.69646919 0.28613933 0.22685145 0.55131477 0.71946897]
 [0.42310646 0.9807642  0.68482974 0.4809319  0.39211752]
 [0.34317802 0.72904971 0.43857224 0.0596779  0.39804426]
 [0.73799541 0.18249173 0.17545176 0.53155137 0.53182759]
 [0.63440096 0.84943179 0.72445532 0.61102351 0.72244338]]
print('最小値 :%f' % np.min(x))
print('最大値 :%f' % np.max(x))
print('平均値 :%f' % np.mean(x))
print('標準偏差:%f' % np.std(x))
最小値 :0.059678
最大値 :0.980764
平均値 :0.524464
標準偏差:0.226296

こちらも同様に式どおりに実装します。np.mean関数が平均(算術平均)、np.std関数が標準偏差を求める関数です。

x_norm = (x - np.mean(x)) / np.std(x)
print(x_norm)
[[ 0.76008999 -1.05315055 -1.31514268  0.11865512  0.86172564]
 [-0.44789519  2.01638476  0.70865548 -0.19236556 -0.58483479]
 [-0.8010976   0.90406276 -0.37955215 -2.05387975 -0.55864464]
 [ 0.94359364 -1.51116753 -1.54227706  0.03132102  0.0325416 ]
 [ 0.48581156  1.43602912  0.88376026  0.38250702  0.87486952]]

正規化後の最小値、最大値、平均値、標準偏差を見てみましょう。

print('最小値 :%f' % np.min(x_norm))
print('最大値 :%f' % np.max(x_norm))
print('平均値 :%f' % np.mean(x_norm))
print('標準偏差:%f' % np.std(x_norm))
最小値 :-2.053880
最大値 :2.016385
平均値 :-0.000000
標準偏差:1.000000

平均値0、標準偏差1にスケールされていることがわかります。また、最小値と最大値も変わっていることにも注意してください。

平均値0、標準偏差1にスケールする仕組みを理解する

Z-scoreについても平均値0、標準偏差1にスケール仕組みをひとつずつ確認していきましょう。

データは先ほどと同じデータを使います。

x = np.array([10, 20, 30, 40, 50])
print(x)
[10 20 30 40 50]

このデータの平均は30、標準偏差は$\sqrt{200}$になります。

print('平均値 :%f' % np.mean(x))
print('標準偏差:%f' % np.std(x))
平均値 :30.000000
標準偏差:14.142136

まずは分子から見ていきます。平均値30を引くので、分子の平均値は0になります。同様に標準偏差求めると$\sqrt{200}$になっています。

print('平均値 :%f' % (x - np.mean(x)).mean())
print('標準偏差:%f' % (x - np.mean(x)).std())
平均値 :0.000000
標準偏差:14.142136

分母は分子の標準偏差と同じ$\sqrt{200}$でしたので、割った結果の標準偏差は1になります。

print((x - np.mean(x)) / np.std(x))
[-1.41421356 -0.70710678  0.          0.70710678  1.41421356]

実際に平均値と標準偏差を求めます。

print('平均値 :%f' % np.mean((x - np.mean(x)) / np.std(x)))
print('標準偏差:%f' % np.std((x - np.mean(x)) / np.std(x)))
平均値 :0.000000
標準偏差:1.000000

外れ値に弱いとはどういうことか

Min-Max normalizationは外れ値に弱いという説明をしました。これがどういうことか、Z-score normalizationなら大丈夫なのかについて具体的な例を使って確認します。

以下のような具体例を用意します。

1,000人分の身長データを使って正規化を行います。身長データは正規分布に従いますが、990件は正常データで10件は外れ値であるとします。
この外れ値は100の位の1が読み取れず欠損したデータ(180cmが80cmになってしまった)が10件あるとします。

まずは必要なパッケージをインポートします。

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

正常データを作成する

まずは正常データ990件を用意します。それっぽい身長データにするために、平均値170、標準偏差10の正規分布に従う乱数を生成します。

np.random.seed(123)
x = np.random.normal(loc=170, scale=10, size=990)

これをヒストグラムで描画してみます。

plt.hist(x, bins=100)
plt.show()

numpy.random.normal関数を使って作成したデータですので正規分布に従っています。

ではこのデータをMin-Max normalization、Z-score normalizationで正規化し、同様にグラフを出力してみましょう。

正常データをMin-Max normalizationで正規化する

まずはMin-Max normalizationを使って正規化します。

x_norm = (x - np.min(x)) / (np.max(x) - np.min(x))
plt.hist(x_norm, bins=100)
plt.show()

最小値0、最大値1にスケールしただけで分布は変わっていません。

正常データをZ-score normalizationで正規化する

次にZ-score normalizationを使って正規化してみます。

x_norm = (x - np.mean(x)) / np.std(x)
plt.hist(x_norm, bins=100)
plt.show()

こちらも中心が0になっているだけで、分布は変わっていません。

外れ値を生成して正常データに加える

では、外れ値を10件生成し、それを正常データに加えていきます。

e1 = np.random.normal(loc=170, scale=10, size=10)-100
print(e1)
[60.91733437 76.55533937 72.40244965 63.547099   73.60026527 51.22368305
 72.34040489 76.24323922 78.11939825 49.77843013]

外れ値は単純に100を引いた値を使います。

このデータを正常値に加え、念のためシャッフルしておきます。

x2 = np.concatenate([x, e1])
np.random.shuffle(x2)
plt.hist(x2, bins=100)
plt.show()

外れ値は小さい値を用意したため、グラフは右に寄った形になっています。

では、これをMin-Max normalization、Z-score normalizationで正規化してみます。

外れ値を含むデータをMin-Max normalizationで正規化する

先ほどと同様の手順で正規化してグラフを出力します。

x2_norm = (x2 - np.min(x2)) / (np.max(x2) - np.min(x2))
plt.hist(x2_norm, bins=100)
plt.show()

外れ値を含まない場合は0.2から0.8くらいの範囲にデータが分布していましたが、外れ値を含むと0.6から0.9くらいの範囲に分布していることがわかります。
もともとは0.5くらいが中心でしたが、0.7から0.8あたりに中心が来ており、データが偏っていることになります。

これが外れ値に弱い(外れ値に敏感ともいう)ということになります。

外れ値を含むデータをZ-score normalizationで正規化する

では、Z-score normalizationだとどうなるでしょうか。

x2_norm = (x2 - np.mean(x2)) / np.std(x2)
plt.hist(x2_norm, bins=100)
plt.show()

こちらもグラフ自体は偏っていますが、外れ値を含まない場合も外れ値を含む場合も-2から2の範囲に分布していることがわかります。
外れ値を含んでいますが、正規化後の分布はほとんど変わっていない、すなわち外れ値に強いということがいえます。

このことから外れ値を含む場合や外れ値を除外できない場合は、Min-Max normalizationよりもZ-score normalizationの方が適しているといえます。
ただし、外れ値のデータを除外したり、外れ値を修正・補完して、想定される最小値・最大値の範囲できるのであれば、Min-Max normalizationを使用して正規化することも可能であることも覚えておいてください。

[NumPy]単位行列を作成する(eye関数、identity関数)

NumPyで単位行列(identity matrix)を作成する2つの方法について解説します。

NumPyには、単位行列を作成する方法が2つ用意されています。

  • eye関数(numpy.eye) – N×Mの単位行列を作成する
  • identity関数(numpy.identity) – 正方行列の単位行列を作成する

N×Mの単位行列を作成するeye関数

単位行列を作成する関数としてよく見かけるのがeye関数です。以下のように書くことで単位行列を作成することができます。

# 3x3の単位行列を作成する
E = np.eye(3)
print(E)

この例では引数に3を指定することで、3×3の単位行列を作成していますが、引数をもう一つ指定することで、正方行列以外の単位行列を作ることもできます。

以下の例では引数に3と4を指定することで、3×4の単位行列を作成しています。

# 3x4の単位行列を作成する
E = np.eye(3, 4)
print(E)
[[1. 0. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]]

正方行列の単位行列を作成するidentity関数

eye関数に比べると知名度はありませんが、正方行列の単位行列を作成することができる、identity関数も用意されています。

先ほどと同様に3×3の単位行列を作成する場合は、以下のように書きます。

# 3x3の単位行列を作成する
E = np.identity(3)
print(E)
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

どのように使い分けるのか

結論からいうと、使い分ける必要はありません。これまでどおりeye関数だけを使用していただいても、eye関数とidentity関数を使い分けても構いません。とりあえず、2つの方法があることだけは覚えておいてください。

モバイルバージョンを終了