OmniAICreator/Qiita-1.07M huggingface dataset:
動画編集、素材、デザインあれこれ

私が社内向けの動画作成で利用しているサイトやフリーソフトをまとめてみました。
※社給PCはWindows10です

#### 動画編集

|タイトル|補足|
|---|---|
|[AviUtl](http://spring-fragrance.mints.ne.jp/aviutl/)|手順 ☞ https://aviutl.info/intro/<br>MP4を認識するためのプラグイン追加必須<br>☞ https://aviutl.info/l-smash-works/<br>MP4ファイルを出力するためのプラグイン（こっちのほうが操作数が断然少ない）<br>☞ https://aviutl.info/kanntann-mp4/<br>動画の知識を付けたい人が読むべきページ【初心者向け】<br>☞ https://aviutl.info/douga-tisiki/| 
|[Openshot Video Editor](https://www.openshot.org/ja/)|AviUtlより導入・操作が簡単|
|フォト(Win既定)|フェードイン・フェードアウトはできない。<br>BGMを途中で変更できない。|


#### 動画素材

|タイトル|補足|
|---|---|
|[Canva](https://www.canva.com/)|| 
|[Pexels](https://www.pexels.com/ja-jp/videos/)||
|[Pixabay](https://pixabay.com/ja/videos/)||
|PowerPoint|・アニメーション等設定したスライドをmp4で保存します<br>・スライドサイズを1920×1080にカスタマイズします<br>　https://pptx.jp/geeks/slide-size/|

#### 写真素材

|タイトル|補足|
|---|---|
|[O-DAN](https://o-dan.net/ja/)|PixabayやUnsplashなど横断検索| 

#### フキダシや矢印、アイコン

以下いずれもトペコンヒーローズ運営サイト

|タイトル|
|----|
|[icooon-mono](https://icooon-mono.com/)|
|[フキダシデザイン](https://fukidesign.com/)|
|[ピクトグラム2.0](https://pictogram2.com/)|

#### イラスト

いらすとやは使いません

|タイトル|補足|
|---|---|
|[ちょうどいいイラスト](https://tyoudoii-illust.com/)|
|[ダ・鳥獣戯画](https://chojugiga.com/)|
|[ぴよたそ](https://hiyokoyarou.com/)|
|[アイソメトリック](https://isometric.online/)|
|[ドッタウン](https://dotown.maeda-design-room.net/)|
|[unDraw](https://undraw.co/illustrations)|HEX変更可能|
|[ソコスト](https://soco-st.com/)|RGB変更可能|
|[Storyset](https://storyset.com/)|海外素材風。RGB変更可能|

* 画像を透過処理する
[PEKO STEP](https://www.peko-step.com/tool/alphachannel.html)

#### フォント

英字本文はSegoe UIを使ってます

|タイトル|
|---|
|[コーポレート・ロゴ](https://fontfree.me/1073)|

#### BGM・効果音

|タイトル|補足|
|---|---|
|[効果音ラボ](https://soundeffect-lab.info/)||
|[DOVA-SYNDROME](https://dova-s.jp/)|| 
|フォト(Win既定)|BGMを途中で変更できない。|


#### （番外編）好きなBGM

##### Bintroll
ほんわかぷっぷー
（じらいちゃんのマイクラ。他の配信者ではぱんくんのAmongUs動画など）

https://dova-s.jp/bgm/play1858.html

レトロパーティー
（動画エンディング）

https://amachamusic.chagasi.com/mp3/retroparty.mp3

##### 先端さん
春のキッチン
（先端恐怖症さんの動画エンディングで利用）

https://dova-s.jp/bgm/play2892.html

2:23AM

https://dova-s.jp/_mobile/bgm/play13513.html

##### ねろちゃん
てくてく

https://dova-s.jp/_mobile/bgm/play3143.html

##### 神宮寺さん
なんでしょう
（考察パートで利用されるBGM）

https://dova-s.jp/bgm/play710.html


##### kakiさん
かえるのピアノ

https://dova-s.jp/bgm/play568.html
特定のディレクトリやファイルを除いてコピーでディレクトリ作成する


コピーでディレクトリを作成する際に、特定のディレクトリやファイルを除外するやり方の覚書き。
cpコマンドではなく、rsyncコマンドを使います。


## 環境

* OS: CentOS7

## 実行フロー

今回は、

* aaaディレクトリからbbbディレクトリを作成する
* .git配下, README.md, 子ディレクトリ下の.gitkeepは除外する

という条件でやってみます


(1) 対象の確認
`-n`オプションにより、実際には実行せず、対象ファイルを確認します（`--dry-run`と同義）
※ターミナルの表示は省略してます

```
$ rsync -ahvn ./aaa/ ./bbb --exclude '.git/' --exclude 'README.md' --exclude '*.gitkeep*'
```


(2) 実行
`--progress`で転送状況を表示させます
※ターミナルの表示は省略してます

```
$ rsync -ahv --progress ./aaa/ ./bbb --exclude '.git/' --exclude 'README.md' --exclude '*.gitkeep*'
```


(3) 結果確認
diffコマンドで確認。除外対象がbbb配下になければOK

```
$ diff -rq aaa bbb
Only in aaa/backup: .gitkeep
Only in aaa: .git
Only in aaa: README.md
Only in aaa/tmp: .gitkeep
```

## 終わりに

本番展開をコピーでやってるので・・、、新規展開 ＆ 不要なものを入れないようメモ。

cpコマンドでは特定のディレクトリやファイルを除く、のは無理っぽいので、rsyncで。
いきなり、rsyncするのはキケンなので、-nオプションで対象を確認した上で、実行してます

## 参考

以下のページたちがとても参考になりました。ありがとうございました。

* [【linux】特定のディレクトリを除いてコピーしたい | SOFTELメモ](https://www.softel.co.jp/blogs/tech/archives/5267)
* [rsyncオプション | Qiita](https://qiita.com/bezeklik/items/22e791df7187958d76c1)
* [diffでディレクトリごと差分をとる | HACKNOTE](https://hacknote.jp/archives/2984/)
DatabricksにおけるPySpark、pandasデータフレームの変換の最適化

[Optimize conversion between PySpark and pandas DataFrames \| Databricks on AWS](https://docs.databricks.com/spark/latest/spark-sql/spark-pandas.html) [2021/6/11時点]の翻訳です。

[Apache Arrow](https://arrow.apache.org/)は、JVMとPythonプロセス間の転送を効率的に行うために、Apache Sparkで使用されるインメモリ、列指向データフォーマットです。pandasとNumpyデータを取り扱うPython開発者にとってメリットがあります。しかし、この機能は自動化されておらず、完全に機能を利用し、互換性を維持するためには、設定、コードに対して若干の修正が必要となります。

# PyArrowのバージョン

PyArrowはDatabricksランタイムにインストールされています。それぞれのDatabricksランタイムバージョンで利用できるPyArrowのバージョンに関しては、[Databricks runtime release notes](https://docs.databricks.com/release-notes/runtime/index.html)を参照してください。

# サポートするSQLタイプ

`MapType`、`TimestampType`の`ArrayType`、そしてネストされた`StructType`以外のすべてのSpark SQLデータタイプは、Arrowベースの変換でサポートされています。`StructType`は`pandas.Series`ではなく、`pandas.DataFrame`として表現されます。`BinaryType`は、0.10.0以降のPyArrowでのみサポートされています。

# PySparkデータフレーム、pandasデータフレームの変換

PySparkデータフレームを`toPandas()`でpandasデータフレームに変換する際、`createDataFrame(pandas_df)`を用いてpandasデータフレームからPySparkデータフレームを作成する際の最適化テクニックとしてArrowを利用することができます。これらのメソッドにおいてArrowを使用するためには、[Spark configuration](https://docs.databricks.com/clusters/configure.html#spark-config)の`spark.sql.execution.arrow.enabled`を`true`に設定します。この設定はデフォルトでは無効化されています。

さらに、`spark.sql.execution.arrow.enabled`による最適化は、Sparkで計算の前にエラーが発生した場合、非Arrow実装にフォールバックする場合があります。Spark設定`spark.sql.execution.arrow.fallback.enabled`によって、この振る舞いを制御することができます。

## サンプル

```py:Python
import numpy as np
import pandas as pd

# Enable Arrow-based columnar data transfers
spark.conf.set("spark.sql.execution.arrow.enabled", "true")

# Generate a pandas DataFrame
pdf = pd.DataFrame(np.random.rand(100, 3))

# Create a Spark DataFrame from a pandas DataFrame using Arrow
df = spark.createDataFrame(pdf)

# Convert the Spark DataFrame back to a pandas DataFrame using Arrow
result_pdf = df.select("*").toPandas()
```

Arrowの最適化を用いた場合でも、Arrowを有効化していない場合と同じ結果を得ることができます。Arrowを用いた場合でも、`toPandas()`はデータフレームにおける全てのレコードのコレクションをドライバープログラムに返却し、小規模のサブセットのデータに対して処理が行われます。

また、全てのSparkデータタイプがサポートされているわけではなく、サポートされていないタイプのカラムがある場合にはエラーとなります。`createDataFrame()`でエラーが発生した場合、SparkはArrowを使用せずにデータフレームを作成します。

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
X'X の列ベクトルが一次独立ならば, X の列ベクトルが一次独立であることを示す

# 最小二乗法の学びなおし【補足】

先日上げた、「[最小二乗法の学びなおし](https://qiita.com/takahashi-ry/items/6bb46cdde473b63379ed)」に以下のような説明を上げました。

>$(X^{T}X)^{-1}$ が存在するということは、$X^{T}X$ が正則であるということ。正則であるということは、$X^{T}X$ の列ベクトルは一次独立であるということ。$X^{T}X$ の列ベクトルが一次独立であるならば、$X$ の列ベクトルもまた一次独立である。

https://qiita.com/takahashi-ry/items/6bb46cdde473b63379ed

この説明に対して、ある方から、「$X$ の列ベクトルが一次独立であるなら、$X^{T}X$ の列ベクトルは一次独立であるのは自明だけど、その逆は成り立つのか計算してみて」といわれたのでやってみました。

# 証明

[証明]

◎$X^{T}X$ の列ベクトルが一次独立ならば, $X$ の列ベクトルが一次独立であることを示す.

```math
\begin{align}
X &= \begin{bmatrix}
x_{11} & x_{12} \\
x_{21} & x_{22}
\end{bmatrix}
とする. \\\\
X^{T}X &= \begin{bmatrix}
x_{11} & x_{12} \\
x_{21} & x_{22}
\end{bmatrix}^T
\begin{bmatrix}
x_{11} & x_{12} \\
x_{21} & x_{22}
\end{bmatrix} \\
&= \begin{bmatrix}
x_{11}^2 + x_{21}^2 & x_{11}x_{12} + x_{21}x_{22} \\
x_{11}x_{12} + x_{21}x_{22} & x_{12}^2 + x_{22}^2
\end{bmatrix} \\
\end{align}
```
$X^{T}X$ の列ベクトルを, $x_{1}, x_{2}$ を使って表す. 

```math
\begin{align}
\boldsymbol{x}_{1} &= \begin{bmatrix}
x_{11}^2 + x_{21}^2\\
x_{11}x_{12} + x_{21}x_{22}
\end{bmatrix} \\

\boldsymbol{x}_{2} &= \begin{bmatrix}
x_{11}x_{12} + x_{21}x_{22}\\
x_{12}^2 + x_{22}^2
\end{bmatrix} \\
\end{align}
```

ここで, $X^{T}X$ の列ベクトルは一次独立であるので,

```math
a\boldsymbol{x}_{1} + b\boldsymbol{x}_{2} = 0 \\
```

となるような, $(a, b)$ は $0$ しかない. 
$x_{1}, x_{2}$ を変形すると

```math
a\begin{bmatrix}
x_{11} & x_{12} \\
x_{21} & x_{22}
\end{bmatrix}^T
\begin{bmatrix}
x_{11}\\
x_{21}
\end{bmatrix} +
b\begin{bmatrix}
x_{11} & x_{12} \\
x_{21} & x_{22}
\end{bmatrix}^T
\begin{bmatrix}
x_{12}\\
x_{22}
\end{bmatrix} = 0 \\

\begin{bmatrix}
x_{11} & x_{12} \\
x_{21} & x_{22}
\end{bmatrix}^T(
a\begin{bmatrix}
x_{11}\\
x_{21}
\end{bmatrix} +
b\begin{bmatrix}
x_{12}\\
x_{22}
\end{bmatrix}) = 0
```
ここで左辺を $0$ にするには $a\begin{bmatrix}
x_{11}\\
x_{21}
\end{bmatrix} +
b\begin{bmatrix}
x_{12}\\
x_{22}
\end{bmatrix}$ が $0$ になればいい. ここで $(a, b)$ が $0$ 以外の解をもつことは, 先ほど導いた $a=b=0$ に矛盾する. すなわち, $a\begin{bmatrix}
x_{11}\\
x_{21}
\end{bmatrix} +
b\begin{bmatrix}
x_{12}\\
x_{22}
\end{bmatrix}$ を $0$ にするような $(a, b)$ は $0$ しかない.

$a=b=0$ ということは, ベクトル $x_{1}, x_{2}$ が一次独立であることを表す.これは$X$ の列ベクトルが一次独立であることと同値である. 
以上のことにより, $X^{T}X$ の列ベクトルが一次独立ならば, $X$ の列ベクトルが一次独立であることが示された.
[終]

【追記】
コメントで成分を用いない証明を @nabla 様が教えてくださいましたので、ここに掲載したいと思います。教えていただきありがとうございます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1272355/ccf64516-44df-2869-2c8e-9f3fd85fa636.png)


# 行列のランクをJuliaで確認

ある行列がフルランクならその行列の列ベクトルは一次独立になります。行列のランクはJuliaで簡単に調べることができます。

以下の行列のランクの例は簡約化した行列の主成分の数から考えるとわかりやすいです。詳しくは、下のリンクの「行列のランク ≦ 行/列の数」についてよく読んでください。

```rank.jl
julia> using LinearAlgebra

julia> A = [
       2 3 4
       4 6 8
       9 1 1
       ]

julia> rank(A) # 2

julia> B = [
       1 3 4
       2 4 2
       4 1 1
       ]

julia> rank(B) # 3

julia> rank(A'A) # 2

julia> rank(B'B) # 3

```

https://risalc.info/src/rank-matrix.html#row
laravel6でlaravel/uiをインストールしようとしたらmmap() failed: [12] Cannot allocate memoryとエラーが出た

はじめてQiitaに投稿します。
間違っている箇所もあるかもしれませんので、あればご指摘どんどんよろしくお願いします！

laravel6でlaravel/uiをインストールしようとしたら予期せぬエラーに出くわしたので、備忘録も含め投稿します。

## 環境

- Windows10
- php7.4.3
- wsl2（Ubuntu）
- laravel 6.20.34

## きっかけ

laravelでlaravel/uiをインストールしようと下記のコマンド実行

```bash
php artisan require laravel/ui "^1.0" --dev
```

## エラー発生

下記のエラーが発生した！！なんじゃこりゃ～！？

```jsx
mmap() failed: [12] Cannot allocate memory

mmap() failed: [12] Cannot allocate memory
PHP Fatal error:  Out of memory (allocated 1029541888) (tried to allocate 20480 bytes) in phar:///usr/local/bin/composer/src/Composer/Json/JsonFile.php on line 288

Fatal error: Out of memory (allocated 1029541888) (tried to allocate 20480 bytes) in phar:///usr/local/bin/composer/src/Composer/Json/JsonFile.php on line 288
```

## 原因

調べたところメモリの確保に失敗したらしい。。。

### phpのメモリ上限を確認

```bash
php -i | grep memory_limit

// php -i はphp.iniの内容をコマンドラインから確認できる=>phpinfo();と同等の内容
```

下記が表示されてphpのメモリ使用量は無制限であることを確認

```bash
php -i | grep memory_limit
memory_limit => -1 => -1
```

### Ubuntuのswap領域が原因の様子

参考サイトより、

> Ubuntuでは標準ではswap領域が設定されていないよう

とのこと。

swap領域を確認

```bash
free -m

total        used        free      shared  buff/cache   available
Mem:           1985         509         958         401         516         942
Swap:             0           0           0
```

参考サイトのとおり、下記コマンドを実行

```bash
// swapの確認（初期状態だと設定なし
cat /proc/swaps
Filename                                Type            Size    Used    Priority

// 管理者権限になる
sudo su -

// swap用のディレクトリを作成
mkdir /var/swap

// 2Gのswap領域を作成
dd if=/dev/zero of=/var/swap/swap0 bs=1M count=2048

// パーミッション変更
chmod 600 /var/swap/swap0

// swap領域の割り当て
mkswap /var/swap/swap0
swapon /var/swap/swap0

// 起動時にswap領域が自動割当するように設定
echo '/var/swap/swap0 swap swap defaults 0 0' >> /etc/fstab
```

これで解決されたので、下記コマンドを再度実行。無事に実行完了

```bash
php artisan require laravel/ui "^1.0" --dev
```

## 参考

[Ubuntu上でLaravelプロジェクトを作る](https://loop-never-ends.com/ubuntu-make-laravel-project/)
TeXShopでコンパイルエラーが出た件

論文を書く練習と思い、TeXを始めたのだが、いきなりエラーにつまずいてしまった。

# エラー内容
```
Fatal format file error; I'm stymied
```
こんなエラーが出てしまい、コンパイルができなかった。

ググったところ、どうやらフォーマットファイルとTeXShopのバージョンが異なっていることが原因のよう。
確かに、書く前にTeXShopのアップデートをした記憶が…


# 解決方法
```
sudo fmtutil-sys --all
```
ターミナルでこのコマンドを打ったら直った。
フォーマットファイルを再作成するコマンドっぽい。

アップデートする度に実行しないといけないっぽいので忘れないようにしなければ。


# 参考
* [【TeX】タイプセット時に Fatal format file error; I'm stymied というエラーが出る](http://coffee.guhaw.com/Entry/772/)
* [Windows64bit版のTeXにfmtファイルを関連付けする方法](https://oku.edu.mie-u.ac.jp/tex/mod/forum/discuss.php?d=2160)
投稿論文Latexの著者注釈、それぞれの所属、メールアドレスの書き方のまとめ


方法１
\author{
    \IEEEauthorblockN{author1\IEEEauthorrefmark{a*}, author2\IEEEauthorrefmark{a,b}, author2\IEEEauthorrefmark{b}}
    \IEEEauthorblockA{\IEEEauthorrefmark{a} School of Computer Science, The University of Tokyo, author, Japan}
    \IEEEauthorblockA{\IEEEauthorrefmark{b} Department of Computer Science and Technology, Tsinghua University, Beijing, China}
    \IEEEauthorblockA{\{author1\}@XXX.com, \{author2,author3\}@XXX.edu.cn}
}


方法2
\author{
    \IEEEauthorblockN{author1$^{a*}$, author3$^{a,b}$, author3$^b$}
    \IEEEauthorblockA{$^a$ School of Computer Science, The University of Tokyo, Tokyo, Japan}
    \IEEEauthorblockA{$^b$ Department of Computer Science and Technology, Tsinghua University, Beijing, China}
    \IEEEauthorblockA{\{author1\}@XXX.com, \{author2, author3\}@XXX.edu.cn}
}
Mapbox for Unity と Maps SDK for Unity の比較

![2021-09-27_100437.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/480f3bcf-4119-d8af-c60f-9ecb8a9c5ca5.jpeg)

**追記 Google Maps Platform ゲームサービスは、2021 年 10 月 18 日に廃止されました**

週末にハッカソンがあり、Mapbox for Unity に触れる機会がありました。
同様に以前 Maps SDK for Unity に触れたことがありましたので、
わかる範囲で比較をしていきたいと思います。

| 項目 |[Mapbox for Unity](https://www.mapbox.com/unity/)|[Maps SDK for Unity](https://developers.google.com/maps/documentation/gaming/install_sdk)|
|---|:---------------------|------------------------|
|動作Unityバージョン(2021年09月時点)|2019.4(2020は不可)|2019.4(2020は不可)|
|導入のしやすさ|サンプルの説明がわかりにくい|サンプルの説明がわかりやすくパラメータを変更して変化を確認しやすい|
|拡張のしやすさ（Script）|機能の把握がやや難しくサンプルの先に進むためのハードルがやや高い。理解するまでにbreakポイントや定義へジャンプする回数が多い|Mapオフジェクトからいろいろな機能にアクセスしやすく機能を追いかけやすい
|2D地図|ズーム/移動なし(Static Map)|あり(Tile Map)。ブラウザでのズーム程シームレスではないが十分実用的|
|3D建物|美しい。正確 |やや簡素。高さは不正確なものが多い。渋谷でも低い建物ばかり|
|3D地形|モデルで構成されており綺麗|簡素。2D地図をメッシュ状にして起伏を付けたもの|
|経路探索|waypoint(key)の数が多くカーブも滑らかに補完|waypointが少なめで緩いカーブの道だとはみ出すことも多い|
|緯度経度/ワールド座標変換|わかりにくい。基本的にMap表示後はWorldベースで行う|わかりやすい。変換用のヘルパー関数が多数用意されている|
|地名/緯度経度変換|かなりあいまいな地名、名所など入力してもちゃんと返ってくることが多い|やや精度は低い|
|ロゴ表示|常に表示|ロゴのON/OFF可能|
|周辺名所検索|あり|なし|
|緯度経度から場所の画像を取得|あり。ユーザー投稿ベースのきれいな画像が大量にある|なし|

所感としては、MapBox for Unityはとても使いやすかったです。
特にハッカソン等短時間で機能を把握する必要がある場合に助かります。
またGoogle程緻密でない分、動作が軽く感じました。

あくまでもハッカソンで軽くさわった程度ですので、間違いなどありましたらご指摘いただけると幸いです。
EC2　pythonで書かれたプログラムをwebからアクセスできるようにする方法

今回は下記MWとアプリケーションを導入して、pythonで書かれたプログラムをwebからアクセスできるようにしていきます。

扱うMW
web(nginx) + ap(uwsgi) 

アプリケーション
Python + Flask(フレームワーク)

〇nginx のインストール・スタート

```nginx
amazon-linux-extras install nginx1
systemctl enable nginx
systemctl start nginx
```

〇Uwsgiのインストール

```
yum install python3-pip python3-devel
yum groupinstall "Development Tools"
pip3 install --upgrade pip
pip install wheel
pip install uwsgi
```
〇Python のインストール

```
yum install git
yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel git
git clone git://github.com/yyuu/pyenv.git ~/.pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
source ~/.bash_profile

#python3をインストールしようとすると、Package libffi was not foundと出てくるのでlibffiをインストールする

yum install libffi-devel
pyenv install 3.7.4
python -V　#バージョンの確認

```
〇uwsgiの設定ファイルを作成する（場所は今回opt配下に作業ディレクトリを作っていますが、どこでもOKです）

```
mkdir -p /opt/hogehoge/uwsgi
cd /opt/hogehoge/uwsgi
vi uwsgi.ini #下記コピペ
~~
[uwsgi]
module = app
callable = app
master = true
processes = 1
http = 127.0.0.1:8000
socket=/opt/hogehoge/uwsgi/app.sock  #unix domain socket を使用しない場合は不要
vacuum = true
die-on-term = true
chdir = /opt/hogehoge/uwsgi
chmod-socket = 666
~~
```

つづいてapp.pyファイルを作成

```
vi app.py　#下記コピペ

~~
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
        return "hello world" #ブラウザを開いたときにhello worldと表示されます。別に文章などはなんでもOK

if __name__ == "__main__":
        app.run()

~~

```

つづいてnginxの設定ファイルを変更する。

```
vi /etc/nginx/nginx.conf

~~
server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
       location / {
       include uwsgi_params;
       uwsgi_pass unix:/opt/hogehoge/uwsgi/app.sock; #UNIX DOMAIN SOCKETを使用する場合はこれ
#       proxy_pass http://127.0.0.1:8000; #←ポート番号で接続する場合はこれ
        }
    }
~~

```

flaskのモジュールをインストール

```
pip install flask
```


ここまでできたらnginxを再起動して、uwsgi.iniを立ち上げる。

```
systemctl restart nginx
uwsgi --ini uwsgi.ini #このコマンドはuwsgi.iniのおいてあるディレクトリにて実行する
```

この状態でwebブラウザからグローバルIPを入力して　hello world が出てくれば成功です。
以下に参考にさせていただいた記事をのせます。

〇Uwsgiのインストール
https://qiita.com/ajitama/items/33639e9622108f03b45a
〇Python のインストール
https://qiita.com/stu345/items/845d88222a8769eb5425
https://qiita.com/t-iguchi/items/f7847729631022a5041f
https://snickerjp.blogspot.com/2016/05/package-libffi-was-not-found.html
〇uwsgi、nginxの設定など
https://teratail.com/questions/297990
〇iniファイルの文法について
https://qiita.com/11ohina017/items/da2ae5b039257752e558
Railsでキーワード＋ ラジオボタンを使った検索方法を実装する

# 1. はじめに
検索機能を実装する課題があったのでまとめます。
調べたところransackというgemを使っても検索機能を実装できるようですが、課題の趣旨から外れてしまうため、今回はgemなしで実装しました。
#2. Railsで検索機能を実装する[View]
**①まずroutesを追加する**
コントローラーとモデルは既に作成済みを想定します。

```rb
get 'search', to: 'XXX#search'
```

私の場合は``/search``というURLにsearchというcontrollerのアクションを対応させました。


**②Viewにform_withで値を設定する**

 ```rb
 <%= form_with(scope: :検索対象のモデル名, url: search_path, method: 'get') do |form| %>
  ・・・
　　　　・・・
 <% end %>
```
・``scope``にはmodel名を指定します。
・urlには``routes``で指定した``controller``のアクションを指定します。
・Railsの推奨で``methods``は``get``になります。

```rb

   <%= form_with(scope: :movie, url: search_path, method: 'get') do |form| %>
	<div class="search">
		<%= form.text_field :search %>
		<%= form.submit "検索する" %>
	</div>
<% end %>

```

後はいつも通り``form``を実装するように値を入れていきます。
``:search ``にはユーザーが検索する際のワードが入ります。

**③絞り込み機能でラジオボタンを実装する**

```rb
 <%= form_with(scope: :paramsで使用する値, url: search_path, method: 'get') do |form| %>
	<div class="radio_button">
		<%= form.label :search_radio,"1", value: 1 %>
		<%= form.radio_button :search_radio, 1	 %>
	</div>
	<div class="radio_button">
		<%= form.label :search_radio,"0", value: 0 %>
		<%= form.radio_button :search_radio, 0 %>
	</div>
<% end %>
```

・valueを設定して文字列をクリックしても結果が反映されるように調整
・``:search_radio``で``controller``に値を送信

# 2.Railsで検索機能を実装する[Controller]

```rb
  def search
      @任意のインスタンス変数 = モデル名.search(search_params)
  end

  def search_params
    params.require(:scopeで設定した値).permit(:search,:search_radio)
  end


```

・``モデル名.search(search_params)``ではモデルに定義してある``search``メソッドを使用して検索を行います。コントローラーで全て済ませることもできますが、ここは好みだと思います。調べたところ、処理方法は主に３つあるようです。

 -　コントローラーで全て処理する
 -　モデルで処理する
 -　新しくモデルを作成してそちらで処理する

コントローラーは肥大化させないという考え方もあるようですし、一般的にはモデルで処理するのではないでしょうか。

・``任意のインスタンス変数``に先程の``モデル名.search(search_params)``を代入してViewで展開できるようにします。

・``search_params``では``View``で設定した値を取得しています。この値はモデルに渡されます。


# 3.Railsで検索機能を実装する[model]

```rb

	def self.search(search_params)
		@任意のインスタンス変数 = モデル名.where( '検索対象カラム名 LIKE ?', "%#{search_params[:search_radio]}%" )
	　　　　　　　　　　　　　　　@任意のインスタンス変数.where( '対象カラム名 LIKE ?', "%#{ search_params[:search] }%" )
       end

```

・``def self.search(search_params)``では設定したパラメーターを受け取っています。
・``@任意のインスタンス変数 = モデル名.where( '検索対象カラム名 LIKE ?', "%#{search_params[:search_radio]}%" )``はラジオボタンの0又は１を検索しています。それをインスタンス変数に代入しています。

・`` @任意のインスタンス変数.where( '対象カラム名 LIKE ?', "%#{ search_params[:search] }%" )``では0又は１が入ったインスタンス変数の中でキーワードを検索しています。

modelでの検索をまとめると

1, パラメーターを受け取る
2, 0又は１(true又はfalse)の結果を変数に代入
3, 0又は１の中でユーザーが入力すたキーワードを検索

#5. おわりに
キーワードとラジオボタンを使用した検索方法を実装するのには苦労しました。最初は条件分岐でキーワードがある場合、ない場合を書かなければいけないのかと思っていましたが、最初にtrue又はfalseを代入してからキーワードで検索する方法は目から鱗でした。いい勉強になりました。
Data Science, ML Projects with Scrum - A Conflict ??

Here, I'm trying to express my opinion on scrum-managed data science projects, machine learning projects. 

Agile project management methods are widely used in the modern software development industry to develop software products. ML, data science projects are data-driven projects. So naturally, scrum seems to be inapplicable. If you do a google search, you can see many data scientist and machine learning engineers doesn't like scrum.

#### Agile - Scrum
A scrum is a product-oriented approach. Start from a primary deliverable product and then give it brush-ups, updates incrementally. This process is proven to be a suitable method for software development. It involves planning, sprints, scrum meetings, and time estimations.

#### Machine learning, Data Science, and Research.
There is a significant difference in data science vs. regular software engineering projects. ML projects can be a successor or can be a failure.  It is difficult to predict from the beginning that this project will be a success, and this project will fail Since we don't know about our data, model accuracy, etc from the start. The only possible thing is trying.

We can divide the data science project into two major phases.

1) Research

- Reading.
- Data gathering.
- Understanding the data.
- Preprocessing.
- Feature engineering.
- Building model.
- Model evaluation.

2) Delivery

- Integration.
- Testing.
- Deployment.
- Monitor.

In my opinion, all things mentioned in phase 1 do not fit into the scrum method. The thing is, we are not dealing with deliverables, and our future actions are not transparent or plannable. So it is difficult(cannot) to commit to something unknown and cannot be assessed. In the research stage, we cannot make progress visible. In that case, we don't have anything to show. We can only show progress after built the model. Sprint time frames also do not fit in this type of project.
We cannot compare building a model to integrate an external API. The path to point A to B is unclear and uncertain. So commitment and velocity measurements are not appropriate. I think only daily meetings are helpful. We can talk about progress and issues. 

But after moving into the second phase, It's standard software engineering flow. Usually, data scientists build models, and then ML engineers deploy it. If the team is large, that approach is possible. But what happens in small teams? You made it, so run it. :D .
But that doesn't mean data scientists shouldn't know other than the building models. In modern it's worth learning few technologies to serve the model via API.

I'm lucky to be in a scrum team with other members who understand situations. Few of my friends living overseas are having some issues with the team due to this scrum data science mismatch. So better understanding among the team members is a critical factor when using scrum for machine learning projects.


*本記事は @qualitia_cdevの中の一人、@nuwanさんが書いてくれました。
*This article is written by @nuwan a member of @qualitia_cdev.
「行列式は自然変換の一例である」について

『ベーシック圏論』で圏論の勉強をしているのですが、行列式は自然変換の一例であるというトピックが色々な概念を含み、あとで圏論の復習をする際に便利な素材だと思いました。

そこで『ベーシック圏論』の文言を引用しながら逐一補足する形で、行列式が自然変換であることを確かめていきます。引用箇所はp34～p35のおおよそ1ページ分です。

> 自然数nを固定する。どのようにn×n行列の行列式が自然変換として理解されるかを見る。

はい。


> 可換環RについてR成分のnxn行列はモノイド$M_n(R)$をなす。

台として「R成分のnxn行列の全体」、モノイド演算として「行列積」を採用する。すると結合則も満たすし、単位は単位行列とすることで単位則も満たすし、モノイド演算について閉じていることもいえる。だから$M_n(R)$はモノイド。

> そして環準同型R→Sはモノイド準同型$M_n(R)→M_n(S)$を誘導する。

RとSが与えられれば、$M_n(R), M_n(S)$ というモノイドも明確に定まる。R→Sなる環準同型が存在すれば、$f: M_n(R) → M_n(S)$ というモノイド準同型もまた存在する、と言っている。さて、そのモノイド準同型はどのようなものだろうか？

$M_n(R)$ の元である行列Xについて、Xの各成分をfで送ることで構成される行列X'は$M_n(S)$ の元である。そこでXをX'に送ればよい。こうすることで、a)単位行列は単位行列に送られるし b)行列積XYをとってから各成分をfで送ってできた行列と、各成分をfで送ってできた行列の行列積が一致する。よってモノイド準同型を構成できた。

> したがって $M_n$: **CRing**→**Mon** は可換環の圏からモノイドの圏への関手を定義している

対象について：任意の可換環 R∈ob(**CRing**) を、モノイドMn(R)∈ob(**Mon**)に送る方法は前述のとおり。

射について：任意の環準同型 f:R→S をモノイド準同型 f':Mn(R)→Mn(S) に送る方法も前述のとおり。

関手公理について：可換環R,S,TがありR→S,S→Tなる環準同型f,gがあったとして、その合成gf:R→Tもまた環準同型だけれど、それを送ったモノイド準同型Mn(gf)は、Mn(g)◦Mn(f)と一致するだろうか？答えはYesだからMnが関手性公理を満たす。証明は行列の各成分を具体的に書き下せばよい。

> また環Rの台集合はモノイドU(R)になりU:**CRing**→**Mon**は別の関手を定めている。

環Rについて加法だけを忘れる忘却関手Uが存在するといっている。

> R成分のnxn行列Xは行列式detR(X)をもち、これはRの元である。

後述の議論のためにはU(R)の台集合の元である（台集合の元と1対1対応する）といった方がいいかもしれない。

> 慣れ親しんでいる行列式の性質 $det_R(I)=1$ $det_R(XY)=det_R(X)×det_R(Y)$ は各Rについて detR: Mn(R)→U(R) がモノイド準同型であることを示している。

前述のようにMn(R)もU(R)もモノイドであるので、その間にモノイド準同型があっても不思議ではない。そして実際存在して「行列式の計算をする」という操作だといっている。

確かに、行列式の計算は、Mn(R)の単位行列をU(R)の1に対応付けるし、Mn(R)上でのモノイド演算後に送った結果（行列積をとってから行列式を計算した結果）は、送ってからU(R)上でのモノイド演算をした結果（行列式を計算してそれらを掛け合わせた結果）と一致している。なるほど行列式の計算はモノイド準同型だった。

> だから射の族$(M_n(R)→U(R))_{R\in {\bf CRing}}$ があって、これが自然変換 det:M_n→Uを定義しているかという問いがありうる。

「自然変換を定義しているか」という言い回しは以下を由来とする。

これまでの議論のように各R∈**CRing**について射 det_R を構成できた。これは自然変換Mn→U (存在するなら名前はdetというのが適当だろう)が存在し、そのR成分/component がdet_Rであるという気にさせる。

> 実際これは自然変換になっている(確認すること！)

自然変換公理は、「R成分行列をR'成分行列にしてから行列式を取った値」と「行列式の値を、R'にした値」が等しいを証明すればよい。

そもそも行列式の定義式は行列成分のかけ算と足し算のみを使った関数として定義されていたのであった。なので上記の自然変換公理はほぼ自明に成立する。

> ...こと！）は行列式が全ての環で一様に定義されていることを反映している。つまりある環上の行列式はこう定義するが別の環については違う方法で定義するということはしない。

前述の証明ではMn(R)上の行列式計算とMn(S)上の行列式計算が同じ形式をしていることが前提だったので、確かにそうだ。

> 一般的に言うと自然数公理は族 $(α_A)_{A∈\mathcal A}$ が $A∈{\mathcal A}$ にわたって一様に定義されているという考え方を捉えていると考えられる。

同じようなものを同じような仕方で定義するという我々の傾向性が、自然性公理という形で一般化・抽象化されているみなせるという見解。傾向性自体が数学的対象ではないので有意な命題ではないけれど、直観的には妥当だとは思える。
最新 KVS の Tkrzw サーバを Docker で起動する

古き良き KVS たる DBM の最新実装である [Tkrzwの連載記事](https://mikio.hatenablog.com/entry/2020/05/16/201836)　を愛読しています。実践的なアーキテクチャ選定・設計の手順と、現代の実装を学べて本当に素晴らしいです。

これまでの [tkrzw](https://github.com/estraier/tkrzw) 本体はローカルの DBM 機能（直接ファイルを読み書きするキーバリューストアのライブラリ機能）だけでしたが、9月になって、[tkrzw-rpc](https://github.com/estraier/tkrzw-rpc) として [gRPC](https://grpc.io/) を利用したサーバ版アプリケーションが動き始めました。

生の Mac では依存管理が少々面倒になるので、手元の Docker で手軽に試せるように Dockerfile を書いてみました。最低限の get・set 手順を紹介します。
https://github.com/kawanet/tkrzw-docker/blob/main/tkrzw-rpc/Dockerfile

## イメージのビルド

```sh
git clone https://github.com/kawanet/tkrzw-docker.git
cd tkrzw-docker
docker build tkrzw-rpc --tag tkrzw-rpc
```

ビルドは数分かかります。
ベースが `ubuntu:focal` なのは、とくに理由はありません。簡単そうなので。
`ubuntu:bionic` だと gRPC パッケージのバージョンが古くて、そのままではビルドできませんでした。
ビルド完了時に、もうちょっと不要ファイルを掃除した方がよいかも。

## サーバの起動

`tkrzw_server` のデフォルトでは、オンメモリのハッシュの [TinyDBM](https://dbmx.net/tkrzw/#overview) のサーバが 1978 番ポートで起動します。

```sh
docker run --name tkrzw-rpc -p 1978:1978 tkrzw-rpc tkrzw_server --log_level debug
```

テーブルごとにサーバを起動していきます。（1サーバで複数テーブル扱えないのかな？）

TinyDBM は揮発性インメモリ DBM なので、サーバが落ちるとデータも消えます。
本番環境なら、下記のような構成にするのかと思います。

- HashDBM か TreeDBM による永続化（ディスクに書き込む）
- マルチスレッド対応（CPUコア数が多ければ速くなる）
- シャーディング対応（テーブルを分割して速くなる）
- マスタ・スレーブのレプリケーション対応（read only レプリカで負荷分散できる）
- デュアルマスタ構成の相互レプリケーション対応（アクティブマスタが落ちてもサービス継続できる）

死活監視とかは自前で用意する必要があるようです。

## とりあえずの get・set

gRPC クライアントの `tkrzw_dbm_remote_util` コマンドでサーバと通信できます。
デフォルトでローカル `127.0.0.1:1978` に接続します。

```sh
docker exec tkrzw-rpc tkrzw_dbm_remote_util set one first
docker exec tkrzw-rpc tkrzw_dbm_remote_util get one
```

これで `first` と表示されれば、成功です。

## ネットワーク越しの get・set

別のインスタンスから接続する場合は、Docker for Mac なら `host.docker.internal:1978` を指定します。

```sh
docker run tkrzw-rpc tkrzw_dbm_remote_util set --address host.docker.internal:1978 two second
docker run tkrzw-rpc tkrzw_dbm_remote_util get --address host.docker.internal:1978 two
```

これで `second` と表示されれば、成功です。
Reactの仕組み

## 仕組みをさらっと理解する
Reactを学ぶにあたって避けては通れない用語や仕組みについて紐解きます。
単なる知識なので知ってるという方はこの＃9は読み飛ばしていただいてOKです。

## Node.jsとBabelとwebpackって？

3つを特徴を簡単にまとめると以下の表の通りになります。

|ツール名|役割|
| --- | --- |
|Node.js|JavaScriptの実行環境を作るJavaScriptエンジン|
|Babel|JavaScriptを変換してくれるトランスパイラ|
|webpack|JavaScriptファイルなどを1つのファイルにまとめてくれるモジュールハンドラ|

詳細に説明していきます。

### Node.js![スクリーンショット 2021-09-17 8.57.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/fc2d7952-8eb7-cf6b-fbc7-03c56a23bd05.png)

ブラウザでしか動作しなかったJavaScriptをサーバーサイドやOS上などブラウザ以外でも実行できるようにしてくれるものです。
Node.jsのバージョン管理には`npm`を利用しています。
Babelを動かすための環境を作る役割も担っています。

### Babel![スクリーンショット 2021-09-17 8.59.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/ee9c68d1-d1de-f00d-8203-34b3fc89e108.png)

ReactのようにJSXで書かれた構文はそのままだとブラウザでは実行されません。
JSXで書かれたコードをブラウザの解釈できる状態に変換してくれます。
また、ES2015以降で登場した記述なども古いブラウザで動作するように変換してくれます。

### webpack![スクリーンショット 2021-09-17 9.00.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/7cd70efd-b9f7-7871-26c6-c589ebcf73d2.png)
複数のJavaScriptファイルをまとめてくれるモジュールハンドラです。
ファイルをまとめることでサイトパフォーマンス、つまり読み込み速度がUPすることが期待できます。
モジュールとは、webpackがまとめたファイルのことで、CSSもそのうちのひとつです。
BabelにはJavaScriptファイルをまとめる機能はないのでwebpackとBabelを組み合わせて利用します。
webpackはSassのコンパイルや画像の圧縮などフロントエンド開発環境を構築することができます。
並列処理がJupyterLabでエラーとなった

# 並列処理をJupyterLabでやるとエラーとなったのでその解決までの手順を記録します
## 参考講座:books::pencil2:
[こちらの講座を参考にしています！](https://datawokagaku.com/multiprocessing/)

##今回のエラー内容:scream:

```
from multiprocessing import Pool, cpu_count
import time

p = Pool(processes=cpu_count())
# sec秒間停止する関数を作成
def wait_sec(sec):
    time.sleep(sec)
    return sec ** 2

before = time.time()
results = p.map(wait_sec, [1, 5, 3])
after = time.time()
print('it took {} sec'.format(after - before))
```
これをJupyterLabで実行したら、

```
Process ForkPoolWorker-1:
Process ForkPoolWorker-2:
Process ForkPoolWorker-3:
Traceback (most recent call last):
Traceback (most recent call last):
  File "/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
    self.run()
Traceback (most recent call last):
  File "/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
    self.run()
  File "/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
    self.run()
  File "/opt/anaconda3/lib/python3.7/multiprocessing/pool.py", line 110, in worker
    task = get()
  File "/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/anaconda3/lib/python3.7/multiprocessing/queues.py", line 354, in get
    return _ForkingPickler.loads(res)
  File "/opt/anaconda3/lib/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/anaconda3/lib/python3.7/multiprocessing/pool.py", line 110, in worker
    task = get()
AttributeError: Can't get attribute 'wait_sec' on <module '__main__'>
  File "/opt/anaconda3/lib/python3.7/multiprocessing/pool.py", line 110, in worker
    task = get()
  File "/opt/anaconda3/lib/python3.7/multiprocessing/queues.py", line 354, in get
    return _ForkingPickler.loads(res)
  File "/opt/anaconda3/lib/python3.7/multiprocessing/queues.py", line 354, in get
    return _ForkingPickler.loads(res)
AttributeError: Can't get attribute 'wait_sec' on <module '__main__'>
AttributeError: Can't get attribute 'wait_sec' on <module '__main__'>
```
というとてつもない量のwarningが出て、30秒待っても実行が終わらないので調べてみるとフリーズしてることに気づく

## 試行錯誤:robot:
調べるとメモリがいっぱいでフリーズしている可能性があるらしい
・ゴミ箱を空にする
・デスクトップにを整理する
・不必要なアプリ、JupyterLab使わないnotebookを、停止する
・Macを再起動する(これやったらDockerがExitになってそれの解決法を探すのに時間かかっってしまった笑
※解決方法は簡単だった)

#### 結果は変わらなかった

## 試行錯誤2:robot:
そもそもJupyterLabで並列処理できない場合があるらしい
VScodeでやってみる

####うまくいかない
if __name__ == "__main__"を使って欲しいとエラーが出てきた


## やっと解決！:relaxed:

```
from multiprocessing import Pool,cpu_count
import time
 
def wait_sec(sec):
    time.sleep(sec)
    return sec**2
 
if __name__ == "__main__":
    p = Pool(processes=cpu_count()-1)
    before = time.time()
    #並列処理
    results = p.map(wait_sec,[1,5,3])
    after = time.time()
    print("It took {}".format(after - before))
    p.close()
    p.join()
    print(results)
```
このコードでうまく実行することができた！
Pythonで10進数→N進数変換とN進数→10進数変換

## N進数から10進数への変換
`int()`関数の第2引数に指定した基数から10進数に変換してくれる

```
print(int('100', 2)) # 4
print(int('100', 3)) # 9
```
2つ目の引数には`2〜36`までの値を指定しないとエラーとなる

## 10進数からN進数への変換

### 2進数、8進数、16進数への変換

```
base10 = 10
print(format(base10, 'b')) # 1010
print(format(base10, 'o')) # 12
print(format(base10, 'x')) # a
print(type(format(base10, 'b'))) # <class 'str'>
```
変換後の型は文字列型である
2進数はbinaryの略で`b`
8進数はoctalの略で`o`
16進数はhexadecimalの略で`x`

### 2進数、8進数、16進数以外への変換
`numpy`をimportして`base_repr関数`を使うと良い

```
import numpy as np

print(np.base_repr(10, 7)) # 13
print(np.base_repr(13, 15)) # D
print(np.base_repr(-130, 20)) # -6A
print(np.base_repr(100, 36)) # 2S
```
MS-Windows 3.1で画面キャプチャ

PC-9801のMS-Windows 3.1の画面キャプチャーの方法をいろいろ模索してみました。

まずWindows 7などにもあるPrt ScはWindows 3.1にもあり、PC-9801ではCOPYキーでクリップボードへのコピーが行われます。WindowのキャプチャはCOPY+GRPHキーでできます。

Winwos 3.1にはクリップボードというアプリがありクリップボードの内容を表示してファイルに落とす事ができます。ファイルの拡張子はclpなのですがクリップボードアプリがないWindows 7では標準では開く事ができません。

![clp24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/feb04d74-296a-f751-6725-547cd15a7583.png)

ネットに画像が入ったclpファイルをpngなどに変換してくれるサービスがありましたが、いちいちネットを開くのは面倒なので、他の方法を考えます。

クリップボードの画像はペイントブラシアプリで貼り付けしてBMPファイルで保存する事ができます。これをWindows 7のペイントなどでpngに変換すればqiitaなどに貼れます。

この方法で一応はできるのですが、ペイントブラシでの保存は切り出さないと余白ができてしまいます。Windows 7のペイントのトリミングのような機能はありません。また元のサイズより大きいキャプチャを貼り付けるとかけてしまいます。

当時のフリーウエアでWinCatchというアプリを見つけました。

https://www.vector.co.jp/soft/dl/win31/art/se006722.html

WinCatchにはVBRJP200.DLLも必要です。

https://www.vector.co.jp/soft/maker/ms/se005296.html

これでBMPファイルを保存すればキャプチャしたサイズで保存されます。

Windows 7のペイントでBMPからpngへの変換はできますが、FreeBSDのパッケージにあるImageMagicのconvertというコマンドラインツールでも変換ができます。SCSI HDイメージはFreeBSD上にありmtoolsで取り出しているのでconvertの方が操作が簡単です。

ImageMagicがclpファイルへの対応しているといいのですが、今のところないようです。誰か作ってください。

WinCatchで保存したBMPをpngに変換してみてみると色がおかしくなってるところがあります。

![x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/5b9237bf-80bf-a433-91f6-7c1873236d82.png)

WinCatchは256色までしか対応しておらず、グラフィックアクセラレータで65536色にしているのが影響しているのかもしれません。

一度WinCatchで保存したBMPをペイントブラシで24ビットビットマップで保存したところ、正しい色で表示されるようになりました。

![x3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/a45ed34b-8336-e696-2bb3-5a5202483c62.png)

もうすこし簡単にできるとよいのですが、そんなに使うこともないので、これで良しとします。
Qiitaのトレンドでよく上位にくる翻訳記事の翻訳元のイカした海外エンジニア向けサイト6選まとめ

#はじめに
Qiitaのトレンドではよく上位に翻訳記事がくることが多いですよね。僕も見つけたらすぐにLGTMとストックの両方をしています。ただ、翻訳記事は**精選されている**、**日本語で読める**などの多くの利点があるのは確かなのですが、自分で大本の記事を探して英語で読むことには**以下の利点**があると思いました。

- **英語の読解力が上がる.**
- **より自分に合った記事を探せる。**
- **多くの記事に触れることで海外の流行を感じ取れる。**

１つ目の利点はエンジニアとしてのレベルを上げていくには必須であると思います。英語を正しく読めることでアクセスできる情報（ドキュメントなど）が大幅に増えるためです。２つ目についてはよりピンポイントにそのときの自分の状況に合った記事を見つけることができるためです。また、偶然の出会い、つまりセレンディピティが期待できます。そして、３つ目は記事を漁っていると感覚としてどんな分野のものが多いとかが肌でわかるようになります。

以上のことから、**エンジニアにとって有益な海外サイト**をまとめました。選定基準はタイトルにあるようにQiitaのトレンドで上位に載りがちな記事の翻訳元の記事が載っているサイトにしました。


英語が苦手な人用にDeepLを貼っておきます。

https://www.deepl.com/ja/translator/l/en/ja

#Better Programming
実用的な技術からエンジニアとしての心構えのような精神的な内容まで、タイトルの通り「より良いプログラミング」をするための良質な記事が載っています。

https://betterprogramming.pub/

#The Smart Coder
スマートなコーダーになるための良質な記事が載っています。要は上記のBetter Programmingと傾向ということです。

https://thesmartcoder.dev/

#Level Up Coding
コーディングのレベルを上げるための良質な記事が載っています。要は（略

https://levelup.gitconnected.com/


#JavaScript in Plain English
タイトルが表すようにJavaScriptに関する多様な記事が載っています。

https://javascript.plainenglish.io/

#Towards Data Science
データサイエンスに関連する内容が中心の記事が載っています。

https://towardsdatascience.com/


#Hacker Noon
サイトが全体的にオタクっぽくて良いです。扱っている話題も広いです。

https://hackernoon.com/

#おわりに
普段のネットサーフィンルーチンに以上のどれかを加えてみるのはいかがでしょうか？より刺激と知識が得られることが期待できます。

Twitterアカウントをフォローしていただけると励みになります!
[@TAQRO_](https://twitter.com/TAQRO_)
CCNA　ACL

では今回はACL(Access Control List)について勉強していきましょう。
これはルータを通過するパケットをチェックし、その通信の制御するために
ACLが使用されます。
許可する通信、拒否する通信に分けることをパケットフィルタリングと言います。

分け方は2種類に分かれます。
1つ目は標準ACLです。
この中でも番号付きと名前付きに分かれます。
これは何で管理するのかということです。
番号だと、1~99,1300~1999と決まっています。
2つ目は拡張ACLです。これは標準ACLより
もっと詳細に制御できます。
こちらも番号付きと名前付きに分かれて、番号付きだと
100~199,2000~2699になります。

では、ACLのルールについて見てみましょう。
抑えて欲しいルールは3つあります。
1つ目はACLは上の行から検索されます。
2つ目は該当する行があったとき、それ以降の行は検索しない。
3つ目は最後の行には暗黙の「全て拒否」が入るという点です。
最終的に当てはまらないIPアドレスを許可する場合は全てのIPアドレスを許可などを
設定する必要があります。

次はACLの作り方を見ていきましょう。まずは番号付き標準ACLからです。
コンフィギュレーションモードで入力します。
access-list[ACL番号][permit/deny][送信元IPアドレス][ワイルドカードマスク]
これはACLを作りなさい。リストの名前はACL番号で、内容はpermit or deny、
フィルタリングの条件は送信元IPアドレスでとワイルドカードマスクです。
ワイルドカードマスクは1のところはチェックしないで、0のところをチェックします。
つまり、192.168.10.1と書いていて、その後にワイルドカードマスクが
255.255.255.０と書いてあるとそれは第３オクテットまでは無視しなさいという意味になります。

その次は名前付き標準ACLについて見ていきましょう。
番号付きと違って、ACLに入り込みます。
ip access-list standard [ACL名]
このコマンドでACLに入り込みます。
その後、[permit/deny][送信元IPアドレス][ワイルドカードマスク]になります。
簡単なワイルドカードマスクの求め方は255.255.255.255から
サブネットマスクを引いた値がワイルドカードマスクになります。

ACLの適用方法について見てみましょう。
2種類の適用方法があります。それはインバウンドとアウトバウンドです。
インバウンドはルータのインターフェイスにパケットが届いた段階で
ACLによるチェックが行われます。
アウトバウンドはルータからパケットが出ていく段階でACLによるチェックが行われます。
また、標準ACLは拒否するトラフィックの宛先近くに設置することが望ましいです。
しかし、拡張ACLは拒否するトラフィックの送信元近くに設置することが望ましいです。
なぜかと言うと、拡張ACLは詳細に設定ができるからです。

拡張ACLの作成について見ていきましょう。
まず、番号付き拡張ACLの場合です。
access-list[ACL番号][permit|deny][プロトコル]
[送信元IPアドレス][ワイルドカードマスク](送信元ポート番号)
[宛先IPアドレス][ワイルドカードマスク](宛先ポート番号)
[オプション]

名前付きACLの場合です。
ip access-list extended [ACL名]
これでACLに入ったあと、[permit|deny][プロトコル]
[送信元IPアドレス][ワイルドカードマスク](送信元ポート番号)
[宛先IPアドレス][ワイルドカードマスク](宛先ポート番号)
[オプション]です。

この中のプロトコルはip,tcp,udp,icmpを指定します。
tcpとudpはパラメータ+ポート番号で表します。
eq(等しい),neq(等しく無い),lt(小さい),gt(大きい)
icmpはecho(エコー要求)とecho-reply(エコー応答)に分かれます。
ポート番号は数字またはポート名で表します。

では、その他の説明に入ります。
logを取得する方法について取り組みます。
logを取得するためには最後にlogを入力します。
これにより、logは見れるようになりますが、
CEFは利用できなくなります。また、CPUの負荷も増えます。
RPiにUbuntu入れてRos2入れるまでのメモ

# ざっくり手順

- MicroSDにUbuntu Server入れる
    - デスクトップ環境は後から入れる
    - Server入れておくと軽いしSSHすぐ使えるし
- WiFiを設定して接続する
- Ros2を公式の"Debian Packages"の手順でインストールする
    - Ubuntuじゃないけどこれで問題なし
    - Raspbianはdebianじゃないのかって思ったけど、まだ試してない

# ハマったとこ

- `Unable to locate package ros-foxy-desktop` 吐かれる
    - インストール手順の"Setup Sources"をちゃんと実行できたかもう一回確認する

- sshログインごとに`source /opt/ros/foxy/setup.bash`必要
    - bashrcに追記する手順を踏んでないから注意

- GPIO UARTがシリアルコンソールで使われてる
    - raspi-config 導入して止める

# 参考リンク

Ubuntu Server 20.04.3 LTS
https://ubuntu.com/download/raspberry-pi

wifi設定の参考
https://qiita.com/kolbe/items/1637a67376ec7c855164

ros2 debian インストール手順
https://docs.ros.org/en/foxy/Installation/Ubuntu-Install-Debians.html#id3

Ubuntu 20.04 (LTS)にraspi-configをインストール
https://qiita.com/penguinprogrammer/items/a252676a3ce6bd1410da
線形回帰モデル vol.5(終)

#前回記事

https://qiita.com/yudaiyamashita/items/b5d09edb377a232fbba6

#参考本

多変量解析入門　小西貞則著

# 幾何的な考察
線形回帰モデルに対して目的変数$y$と$p$個の説明変数$x_1, x_2, \cdots , x_p$に関して観測された$n$個のデータを,　$\{ (y_1, x_{i1}, x_{i2}, \cdots, x_{ip}: i= 1,2, \cdots ,p) \}$とする. 

この観測データに基づく線形回帰モデルは, ベクトルと行列で表記すると

$$\boldsymbol y = X \boldsymbol \beta + \boldsymbol \epsilon$$

で与えられた. 

計画行列$X$(n $\times$ p+1の行列)を縦ベクトルで構成すると

$$X= [\boldsymbol 1, \boldsymbol x_1^{*}, {\boldsymbol x_2^\ast}, \cdots, \boldsymbol x_p^\ast]$$

$\boldsymbol 1$は要素全てが$1$の$n$次元ベクトル。

$$\boldsymbol y=[\boldsymbol 1, \boldsymbol x_1^{*}, {\boldsymbol x_2^\ast}, \cdots, \boldsymbol x_p^\ast] \boldsymbol \beta+\boldsymbol \epsilon=\beta_0\boldsymbol 1+\beta_1\boldsymbol x_1^\ast +\beta_2\boldsymbol x_2^\ast+ \cdots + \beta_p\boldsymbol x_p^\ast$$

（[]は行列を表す記号。縦ベクトルを集めた行列と縦ベクトルを同順で掛けると2番目の＝の通り、変形できる。計算で確かめられる。)

と表すことができる。右辺の式は、計画行列Xの$(p+1)$個の$n$次元列ベクトルによって張られる以下の$(p+1)$次元部分空間を構成する。

$$M(X) = ( x^\ast : x^\ast = \beta_0\boldsymbol 1 + \beta_1 \boldsymbol x^\ast_1 + \beta_2 \boldsymbol x^\ast_2 +\cdots \beta_p \boldsymbol x^\ast_p)$$
(集合の記号が上手く出力されないので（）に変えました)
（説明変数ベクトル$\boldsymbol x$と違うぞ、ということを表すため、アスタリスクをつけました。）

回帰係数を変化させると$X\boldsymbol \beta$は線形部分空間$M(X)$を移動することがわかる。

![rect833.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698527/875116e2-7bc5-2636-cd17-cdc07c3650e4.png)





最小2乗法とは誤差の2乗和$\epsilon^T \epsilon = (\boldsymbol y - X\boldsymbol \beta)^T (\boldsymbol y - X\boldsymbol \beta)$を最小とする回帰係数$\hat{\boldsymbol \beta}$を推定値とする方法であった。したがって$\boldsymbol y = X \boldsymbol \beta + \boldsymbol \epsilon$に対し、誤差の2乗和、L2ノルムが最小になるのは、部分空間$M(X)$上を移動する$X\boldsymbol \beta$が$n$次元観測値ベクトル$\boldsymbol y$から線形部分空間上への射影になるときである。

残差ベクトル$\boldsymbol e$は$\boldsymbol y$から$M(X)$へ下ろした垂線である

よって$\boldsymbol y^T\boldsymbol y=(\hat{\boldsymbol y}+\boldsymbol e)^T(\hat{\boldsymbol y}+\boldsymbol e)=\hat{\boldsymbol y}^T\hat{\boldsymbol y}+\boldsymbol e^T\boldsymbol e$($\hat{\boldsymbol y}$と$\boldsymbol e$は直交する)

$\boldsymbol y= \hat{\boldsymbol y}+\boldsymbol e$, この射影$\hat{\boldsymbol y}$は$n \times (p+1)$次計画行列$X$の列ベクトルで張られる$p+1$次元線形部分空間$M(X)$上のベクトルであることから, 射影行列$X(X^TX)^{-1}X^T$によって, 

$$\hat{\boldsymbol y}=   X(X^TX)^{-1}X^T\boldsymbol y$$

これは$$X\hat{\boldsymbol \beta}$$

とイコールであり、まさに
$\hat{\boldsymbol \beta}= (X^TX)^{-1}X^T\boldsymbol y$であり、幾何的な意味で、最小2乗推定値を与える回帰係数ベクトルであることが示された. 

$M(X)$の直交補空間$M^{\bot}(X)$(3次元空間であれば直線)
#　射影行列
$n$次元ベクトル$\boldsymbol y$とその射影$\boldsymbol x$に対して、$P\boldsymbol y= \boldsymbol x$というような$n \times n$行列$P$を射影行列という。
$I-P$は直交補空間$M^{\bot}(X)$上への射影行列である。
$P$は、$P^2=P$を満たす。

※射影する寄与を2回かけても元の1回分の寄与だけ、効き目がある。
#線形部分空間上への射影行列
$n\times (p+1)$行列$X$の階数を$p+1$とすると、$X$の$p+1$個の列ベクトルで張られる線形部分空間上への射影行列は$P=X(X^TX)^{-1}X^T$で与えられる.
$$(I - X (X^TX)^{-1}X^T)^2=I - X (X^TX)^{-1}X^T$$
であるから云々
Rubyの「宇宙船演算子」を再定義して AtCoder ABC219C を解く

AtCoder の ABC219C の問題は以下のリンクから参照下さい。

https://atcoder.jp/contests/abc219/tasks/abc219_c

簡単にいうと、アルファベットの順番を替えた上で、複数の文字列を辞書順にソートして出力するという問題です。

じつはこれ、とても Ruby らしい簡潔な解法が存在するので、まずはそちらを説明します。

```ruby:Ruby
x = gets.chomp
n = gets.to_i
strs = n.times.map { gets.chomp }

puts strs.sort_by { _1.tr(x, "a-z") }
```

`x`は新しいアルファベットの順番、`strs`はソートすべき文字列の入った配列で、`sort_by`を使って、解いている部分は1行で書けています。かかる時間もかなり速いです。

#本題

さて、`Array#sort`は配列の中身が文字列の場合、暗黙にいわゆる「宇宙船演算子」、つまり`String#<=>`を使って辞書順にソートします。では、これを再定義してやれば、ただの`Array#sort`でいけるのではないかという、変態的発想をもちました。

```ruby:Ruby
x = gets.chomp
n = gets.to_i
strs = n.times.map { gets.chomp }

$table = x.each_char.map.with_index { |c, i| [c, i] }.to_h

class String
  def <=>(other)
    l = [length, other.length].min
    (0...l).each do |i|
      a = self[i]
      b = other[i]
      if a != b
        return $table[a] <=> $table[b]
      end
    end
    length <=> other.length
  end
end

puts strs.sort
```

「辞書順」の定義は、問題文の「辞書順とは？」にあるアルゴリズムをそのまま実装しました。グローバル変数なぞ使って、美しくないコードですね。時間も上の簡潔な解法に比べ、10倍ほどかかっています。メリットはまるでないのですが、オープンクラスを使い、組み込みの比較用のメソッドを再定義するという、極め付きの動的言語である Ruby 以外ではちょっと不可能な解法ではないでしょうか。実際、ソートはただの`Array#sort`で済んでいますね。お粗末様でした。
【pyenv-win】pyenv を使って Anaconda をインストール

## 環境
+ Microsoft Windows 10.0.19042.1165
+ Git 2.33.0.windows.2（コマンドは Git Bash で実行）
+ pyenv 2.64.11


## 現象
pyenv に Anaconda をインストール方法を調べると、`pyenv install -l` でインストールできるバージョンを確認し、`pyenv install anaconda3-5.3.1` のようなコマンドを実行することでインストールできるようだ。しかし、`pyenv install -l` を実行しても 'anaconda' の文字が見当たらない。

## 原因
pyenv-win が Anaconda のインストールをサポートしていないのが原因のようだ。

>  kirankotari commented on 25 Jul 2020
we are not supporting this feature yet. Need sometime.（[link](https://github.com/pyenv-win/pyenv-win/issues/25#issuecomment-663604259)）

[このスレッド](https://github.com/pyenv-win/pyenv-win/issues/25)に解決策が書かれているため、その方法を試すことにする。

## 解決策
> oryon-dominik commented on 8 Aug 2020
As a workaround: you could manually install miniconda (or another conda version) into the versions directory. Pyenv-win recognises any python that lives in there. You can then set `pyenv local miniconda`（[link](https://github.com/pyenv-win/pyenv-win/issues/25#issuecomment-670830919)）
（訳：回避策として：miniconda（または別の conda バージョン）を versions ディレクトリに手動でインストールできる。pyenv-win は、そこに存在するすべての Python を認識する。それから、`pyenv local miniconda` を設定できる）

## インストール手順
### 1. Anaconda インストーラをダウンロード
こちらからダウンロード

https://www.anaconda.com/products/individual

### 2. Anaconda のセットアップ
インストールするフォルダパスを `C:\Users\USERNAME\.pyenv\pyenv-win\versions\Anaconda3` に設定（ `Anaconda3` の部分はインストールするものによって変わる）
![スクリーンショット (229)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1946472/8dd76c62-8018-25de-e10b-3aebc129b858.jpeg)

### 3. pyenv で設定

```powershell
pyenv global Anaconda3
```

インストールされていることを確認

```powershell
pyenv versions
```

このように出力されれば OK

```powershell
  3.9.6
* Anaconda3 (set by C:\Users\USERNAME\.pyenv\pyenv-win\version)
```

## 参考

https://ferhonojiyutyo.net/2021/07/12/windows%E3%81%AEanaconda%E3%82%92pyenv-win%E3%81%A7%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B/
AccessのリッチテキストフィールドのリッチテキストをHTMLに変換する

# はじめに
古いAccessのデータがRichTextで保存されていて、これをHTMLに変換する方法。

# 1.環境
WordのSaveAs2でwdFormatHTMLオプションを使って変換するのでAccessとWordの両方が必要。使った環境は以下の通りだが、Access/Word 2003以上のバージョンでもたぶん動作すると思う。（検証はしていない。）

- Access 2016
- Word 2016
- Windows 7



# 参考記事
- [Convert rtf format to html format](https://social.msdn.microsoft.com/Forums/ja-JP/141fb4cb-5dd8-4b01-bd83-8146a05cf54f/convert-rtf-format-to-html-format?forum=accessdev)
- [Copy RTF text from Access to word table using VBA](https://stackoverflow.com/questions/16350527/copy-rtf-text-from-access-to-word-table-using-vba)
- [Range. InsertFile メソッド (Word)](https://docs.microsoft.com/ja-jp/office/vba/api/word.range.insertfile)
WSHでマウスを操作する

# 概要

WSHで実現が難しいものの一つに**マウス操作**があります。

打開策として

1. Excelを経由してWin32APIを叩く
2. rundll32.exeで利用できるラッパーDLLを作成する

などが考えられます。

今回は2の手法について、Win32APIの汎用入力関数である[SendInput](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)のラッパーDLLを作成して、マウス操作を実現します。

コードとバイナリは[こちら](https://github.com/mukai1011/SendInput-wrapper)

# 従来の手法からの改善点

予め用意したDLLをrundll32.exeで叩くマウス操作は、こちらでも紹介されています。

https://language-and-engineering.hatenablog.jp/entry/20081117/1226943698

http://jscript.zouri.jp/Source/MouseCtrl.html

これらのDLLは [SetCursorPos](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setcursorpos) / [mouse_event](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event) を利用しており、マルチディスプレイ環境で期待した動作をしない(プライマリディスプレイ内しか移動できない)という課題がありました。

SendInputを利用することにより、セカンダリ以降のディスプレイにもカーソルを移動させることが可能です。

また、入力には[INPUT構造体](https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input)を模したJSON形式の文字列を採用しました。もっともJScriptで利用できるJSONは、stringifyに一癖ありますが...

# サンプル

円形にマウスを移動させるサンプルです。

SendInput.dllをスクリプトと同じディレクトリに配置して、cscriptで実行してください。終了はCtrl+Cです。

```javascript
var global = Function('return this')();
if (!global.JSON) {
    var html = new ActiveXObject('htmlfile');
    html.write('<meta http-equiv="x-ua-compatible" content="IE=11" />');
    global.JSON = html.parentWindow.JSON;
    html.close();
}

var oWshShell = new ActiveXObject("WScript.Shell");

var INPUT_MOUSE = 0;
var MOUSEEVENTF_MOVE = 0x0001;
var MOUSEEVENTF_ABSOLUTE = 0x8000;

var angle = 0;

WScript.StdOut.Write("");
while(true) {
    var x = 500 + Math.floor(Math.sin(angle) * 100);
    var y = 500 + Math.floor(Math.cos(angle) * 100);

    var input = {
        "type": INPUT_MOUSE,
        "mi": {
            "dx": x,
            "dy": y,
            "dwFlags": MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
        }
    };
    oWshShell.Run('rundll32 SendInput.dll,_SendInput ' + JSON.stringify(input));
    WScript.StdOut.Write("\r" + x + "," + y);

    angle = angle < 2 * Math.PI ? angle + 0.1 : 0;
}

```

# TODO

- 各種定数を定義する
- INPUT構造体(を模したJSON)の配列に対応させる。複数イベントの同時送信
  - 配列のstringifyが配列にならないので困る、より本格的なpolyfillを導入することで解決できるかも
    - 参考: https://www.bugbugnow.net/2018/05/wshjscriptjson.html

全体的にあまり確認が済んでいないため、バグなど見つけたらPRください。

https://github.com/mukai1011/SendInput-wrapper
DockerfileからDockerimageへのコマンドやINSTRUCTIONについて

# はじめに
本記事では、DockerfileからDockerimageへのコマンド達をまとめました。
本日学習した範囲内で、アウトプットします。

先日もコマンドについて投稿しているのでこちらも参照ください。

https://qiita.com/mkato1013/items/7a9be0a8ce16ed0f1ba9

# Dockerfile
DockerHubから例えば`ubuntu`を検索すると、
当然`ubuntu`が最初に出てくるはずです。
[ubuntu](https://hub.docker.com/_/ubuntu)
ここから、
`Supported tags and respective Dockerfile links`
というところの一番上のリンクに入ると、`Github`に行けます。

ここには、`DockerfileからDockerimage`を作るためのコマンドは乗っています。

```docker
FROM scratch
ADD ubuntu-bionic-oci-amd64-root.tar.gz /
CMD ["bash"]
```

`FROM`とか`ADD`とかは、INSTRUCTION
それら以降に記述するものは、argumentです。

### docker build .
dockerがDockerfileを探してDockerimageを作ってくれます。
配下のDockerfileでimageを作ることが多いので、
基本的に`.`を最後におきます。

また、imageを作っただけなので、
今のところでは、ダングリングのimageになっています。`<none>`です。

### docker images -f dangling=true
ダングリングのimageだけを表示する。

### docker build -t つけたい名前 親のディレクトリー
`docker images -f dangling=true`で、
表示したimageに名前をつけられる。

```
例
% docker build -t new-ubuntu:latest .

% docker images
REPOSITORY               TAG       IMAGE ID       CREATED         SIZE
new-ubuntu               latest    ~~~~~~~~~~~~   1 minutes ago   65.6MB
```

## DockerfileのINSTRUCTIONについて
### FROM
ベースになるimageを決定する。
`ubuntu`とか`alpine`とか。
あまりデータ容量が大きいものを使うと後々容量過多で重くなるので注意。

### RUN
これを使うと好きなようにカスタマイズできる。
`RUN touch test`で`testファイルを作る`

また、RUNするごとに、layerができる。
`RUN`を連発してしまうと、layerだらけになり容量過多になるので注意。

- コマンドを`&&`でつなげる
- バックスラッシュを使って改行する

という対策をとる。

### CMD
`コマンド`の略。
コンテナにてLinuxのデフォルトのコマンドを指定できる。

CMD ["コマンド","(あれば)コマンドの引数１","(あれば)コマンドの引数2"]
bashを起動する場合が多いと思うので、
`CMD ["bin/bash"]`が多いと思います。

原則`Dockerfileの最後`に記述。


以上です。

# 終わりに
Dockerfileの基礎を学習していました。
理解できると楽しいですね〜。

私の学習している動画はこちらです。(有料)
最高に分かりやすいのでオススメです。
[米国AI開発者がゼロから教えるDocker講座](https://www.udemy.com/course/aidocker/)

明日もDocker学習頑張ります！