OmniAICreator/Qiita-1.07M huggingface dataset:
初心者によるプログラミング学習ログ 199日目

#100日チャレンジの199日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

199日目は

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おはようございます<br>199日目<br><br>udemyでCSS+メディアクエリ、vue.js、webサイトコーディング<br><br>なんかいろいろやった。<br>Rubyもやろうとおもったけど、まだ新しい言語に手をだすのはやめといた<a href="https://twitter.com/hashtag/%E6%97%A9%E8%B5%B7%E3%81%8D%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8?src=hash&amp;ref_src=twsrc%5Etfw">#早起きチャレンジ</a><a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a><a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアと繋がりたい</a></p>&mdash; ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) <a href="https://twitter.com/yudapinokio/status/1211770817249308672?ref_src=twsrc%5Etfw">December 30, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
サポートベクトルマシンを理解する。カーネルトリックについても理解する。

サポートベクトルマシンで非線形問題を解くときにカーネルトリックというものが使われますが、ざっくり本質を理解するのに骨が折れました。間違っているかもしれませんが、私なりに理解したことを整理しておこうと思います。

# 分かっている特徴量で散布図を描いた場合に、明らかに線形分離可能な場合

ふつうのサポートベクトルマシンの話です。

サポートベクトルマシンでは、マージンを最大化するための直線を明らかにするために、重み$w$とバイアス$b$を求めたいわけですが、それを真っ向から解くのは難しいです。

そこで、双対問題と言われる、別の問に置き換えます。置き換えに利用されるのが、ラグランジュの未定乗数法です。いかにも難しそうですね。ここでは詳細には立ち入りませんので、ご安心ください。

どんな問題に置き換えるかというと、色々と小難しい変換を経て、最終的には以下で構成される関数を得ます。

* $α$というパラメータ　（ラグランジェ乗数と呼ばれます）
* $x^{(i)\mathrm{T}}x^{(j)}$ （分かっている特徴量の内積）

この関数を最大化する$α$を求める、という問題に置きかわります。

後者の$x^{(i)}$ と $x^{(j)}$ は、それぞれ別々の特徴量です。アヤメ(iris）の話でいうと、「がく片の長さ」と「花弁の長さ」みたいなものですね。

後者については実際のデータがあるわけですから、未知なのは前者の$α$だけです。後者に実際のデータを代入すると、$α$についての方程式になりますので、簡単に$α$の値が分かります。その$α$をもとに、重み$w$とバイアス$b$を導出し、マージンを最大化する直線が明らかとなります。

# 分かっている特徴量で散布図を描いた場合に、明らかに線形分離できない場合

この場合、分かっている特徴量から、新たな特徴をつくって、その新たな特徴量をつかって表現する座標空間上で、線形分離を試みます。

この場合も、色々と小難しい変換を経て、最終的には以下で構成される関数を得ます。

* $α$というパラメータ　（ラグランジェ乗数と呼ばれます）
* $\phi(x^{(i)})^{\mathrm{T}}\phi(x^{(j)})$ 

さきほどの $x^{(i)\mathrm{T}}x^{(j)}$ が、$\phi(x^{(i)})^{\mathrm{T}}\phi(x^{(j)})$ に置き換わっただけです。

$\phi()$は、分かっている特徴量から新たな特徴量を算出する関数です。本当は、この関数がものすごく大事なはずです。なぜなら、新たな特徴量がどんなものかによって、新たな座標空間上で線形に分離できるかどうか決まってくるわけですから。

理論上はそうなんですが、以下の問題が生じます。

* ふつうは、この$\phi()$という関数を見つけるのが大変だと思われます。
* $\phi()$が分かったとしても、さきほどの内積 $\phi(x^{(i)})^{\mathrm{T}}\phi(x^{(j)})$ を計算しようとすると、ものすごい計算コストになります。

この二つの問題を解決するのが、いわゆる「カーネル関数」です。

カーネル関数は $\phi(x^{(i)})^{\mathrm{T}}\phi(x^{(j)})$ を置き換えるもので、いくつか種類があります。

$\phi()$ がどんなものであったとしても、$\phi(x^{(i)})^{\mathrm{T}}\phi(x^{(j)})$ の計算結果は、カーネル関数での計算結果と同じになります。言い換えると、$\phi()$ の内容については考えなくてもよいわけです。すごいですね。これが「トリック」と呼ばれる１つ目の理由です。

そして、カーネル関数は計算コストが低いです。これが「トリック」と呼ばれる２つ目の理由です。たとえば代表的なガウスカーネル（RBFカーネル）は、以下のとおりです。

```math
exp(-\frac {||x^{(i)} - x^{(j)}||^2} {2\sigma^2})
```

二つの特徴量$x^{(i)}$ と $x^{(j)}$ と、ハイパーパラメータ$2\sigma^2$ （我々が自由に決めてよいパラメータ）から、$\phi(x^{(i)})^{\mathrm{T}}\phi(x^{(j)})$ の計算結果が、簡単に得られてしまいます。

あとは、線形分離できる場合と同様に、$α$を求め、それをもとに重み$w$とバイアス$b$を求めます。重み$w$とバイアス$b$によって定義される面こそが、新たな座標空間上で、マージンを最大化する面となります。

なお、カーネル関数には種類があり、どれを選ぶかによって結果が変わってきます。実際、scikit-learnなどのライブラリでは、サポートベクトルマシンを簡単に利用できますが、そのAPIでカーネル関数を指定できるようになっています。

# 参考
https://kenyu-life.com/2019/04/22/svm_kernel/
https://www.slideshare.net/moa108/8-28571831
Power BIカスタムビジュアル「Timeline Slicer」

## はじめに

　PowerBIのカスタムビジュアルより、ユニークなスライサー（時間によるデータの絞り込み、フィルター）「Timeline Slicer」を紹介します。
　[「できるPower BI」](https://qiita.com/ishiayaya/items/38c0c03c626bc048f524)の関連ページは、レッスン38（P158）です。

## カスタムビジュアルはどこから

　画面の「視覚化」の「・・・」から、マーケットプレイスを呼び出します。

![カスタムビジュアルとは.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/cca15c9a-d6eb-9b61-3af0-16dc50c28522.png)

マーケットプレイスでは「Timeline」で検索します。

![スクリーンショット 2019-12-31 06.39.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/081cbf49-f083-07dc-5003-e72a1516ee5c.png)


[「Timeline Slicer」はマイクロソフトのPower BIチームにより作成](https://powerbi.microsoft.com/ja-jp/blog/visual-awesomeness-unlocked-the-timeline-slicer/)されたものなんですね。シンプルでわかりやすく流石です。


## 使い方
　日付によるフィルターなので、注文日等日付のデータをセットするだけです。
![写真 2019-12-31 6 42 13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/c3718f05-4870-7a76-db4e-7d054678c871.jpeg)


あとは端っこを引っ張って範囲を変えられます。クリアは空白をクリックで。

![写真 2019-12-31 6 41 59.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/07bc2160-1857-344b-a160-08379248041a.jpeg)


左上の単位変更のところで、年指定、四半期指定等変更できます。

![写真 2019-12-31 6 41 45.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/c9535797-66be-04f0-de59-789bb6054d59.jpeg)
入れたてubuntuにtensorflow-gpu環境を構築する方法

# はじめに
久々にUbuntuを壊してしまい、再インストールしました。
やはり環境構築は難しいですね。
今回は、0からtensorflow-gpu環境を構築するための方法を残しておきますので、初心者の方は参考にできるかと思います。

また、各パッケージのバージョンはどんどん更新されると思います、より新しいものを利用したい場合は、公式のドキュメントを見るか、他の方のページを見るか、本ページの流れにそいながらバージョンだけ変えながらやればできると思います。

# 動作環境
`Ubuntu18.04LTS`を用いた。日本語版isoは[こちら](https://www.ubuntulinux.jp/News/ubuntu1804-ja-remix)。
既にUbuntuを使用している方は`Make Startup Disk`というアプリを使ったら良いが、初めての方は`UUI`等のフリーソフトを使用する必要がある（はず）。

# 動作確認方法
tensorflowでは、[こちら](https://www.tensorflow.org/api_docs/python/tf/test/is_gpu_available?version=stable)に記載されているとおり、GPUが使用可能かどうかが以下の関数で確認できる。

```sh:
import tensorflow as tf
tf.test.is_gpu_available()
```

# 各バージョン
tensorflow-gpuのページに３つともの依存関係（？）が掲載されていたため、[こちら](https://www.tensorflow.org/install/source#common_installation_problems)のページ下部に記載されている以下のバージョンをインストールした。

| tensorflow_gpu-1.14.0 | 2.7, 3.3-3.7 | GCC 4.8 | Bazel 0.24.1 | 7.4 | 10.0 |
|:--|:--|:--|:--|--:|--:|

# tensorflowのバージョン
今回は、`1.14.0`をインストールした。

```sh:
pip3 install tensorflow-gpu==1.14.0
```

ここで、pip3のエラーが出る場合は、`$ sudo apt install python3-pip`とすると良い。

# NVIDIAドライバのインストール
GPU付きPCにUbuntuをインストールする際は必要になることが多い。[こちら](https://www.nvidia.co.jp/Download/index.aspx?lang=jp)の入力欄に自分のマシンに合ったものを選ぶ。

NVIDIAドライバのインストールは苦戦することが多い。手順は以下。

1. [こちら](https://www.nvidia.co.jp/Download/index.aspx?lang=jp)からNVIDIAドライバの`.run`ファイルをダウンロード
2. Ctrl+Alt+F1を押す
3. Ctrl+Alt+F3を押す
4. ユーザ名とパスワードでログイン
5. `$ sudo service gdm stop`
6. `$ cd ~/Downloads`
7. `$ sudo bash ./NVIDIA-*.run`

うまく行かないようであれば、`$ reboot`で再起動をしてもう一度試すとうまく行くことがある（GPUが使用中みたいなことが出た場合）。

# cudaのインストール
[こちら](https://developer.nvidia.com/cuda-10.0-download-archive)から`runfile(local)`をダウンロードしてきて実行する。

```sh:
$ cd ~/Downloads
$ sudo bash ./cuda_10.0.*_linux.run
```

# cuDNNのインストール
cuDNNをインストールする場合は、NVIDIAアカウントを作成する必要がある。[こちら](https://developer.nvidia.com/rdp/cudnn-download)でログインするとcuDNNダウンロードページに移動できる。ここでは、規約に同意する必要がある。

今回は、過去のバージョンをインストールするため、`Archived cuDNN Releases`をクリックしてほしいバージョンまで移動する必がある。`.deb`ファイルをダウンロードしてきたため、`Ubuntu Software`というアプリケーションを使用してパッケージをインストールした。これは`Files`で`.deb`ファイルの場所へ移動して、ダブルクリックすると起動してくれる。

# 全部インストールしてもGPUを識別しない！！
私がよくやってしまうのがこれである。インストールだけして満足しているとPATHが通っていないため、`$ nvcc -V`が動作しない。また、tensorflowを読み込む際にはcudaのPATHが読み込まれている必要があるため、tensorflowからもGPUが識別されない。
そこで、`~/.bashrc`に以下を追記する必要がある。

```conf:.bashrc
export PATH=$PATH:/usr/local/cuda/bin
export LD_LIBRARY_PATH=/usr/local/cuda/lib64
```

# 参考
以下のページを参考にさせていただきました。困った場合は、是非こちらを参考にしてみてください。
- https://towardsdatascience.com/how-to-make-tensorflow-work-on-rtx-20xx-series-73eb409bd3c0
    - export忘れの参考
FirebaseでAuthenticationで登録されたユーザーを取得する


[本家サイト](https://firebase.google.com/docs/auth/admin/manage-users?hl=ja)にサンプルがあるが、async/await形式で試す。が、専用のバッチ処理で取り出す必要があるようで、以下のような特徴（制限）があるよう。

* 一度に取得できる最大数は1000件
* 取得時に条件での絞り込みはできない（全部取得する必要がある）

なので1000件以上のデータやCustom Claims等で絞り込みたい場合はクライアント側で工夫する必要がある。
以下はその例。

1000件以上あれば再帰的に処理。取得後、admin==trueとなっているユーザーを取得。


```js
const admin = require('firebase-admin');
admin.initializeApp({
    credential: admin.credential.cert('/path/to/key.json'),
    databaseURL: 'https://xxxxxxxxxx.firebaseio.com',
});


//再起処理するためにカスタム関数作成
const listAllUsers = async (users = [], nextPageToken) => {

    try {
        //listUsers()実行
        const res = await admin.auth().listUsers(1000, nextPageToken);
        //取得した結果を配列に入れる
        users = res.users.map(user => user.toJSON());
        //page.tokenはundefinedじゃなければ再帰的に実行
        if (res.pageToken) await listAllUsers(users, res.pageToken);

    } catch (e) {
        console.log(e);
    }
    //usersを返す
    return users;
}

(async () => {

    //実行
    const users = await listAllUsers();
    const admins = users.filter(user => user.customClaims.admin === true);
    admins.map(user => {
        console.log(user.uid);
    })

})();
```

うーん。
いろいろクエリ投げたい場合は登録と同時にユーザー用のcollection等を作るほうが便利か・・・（でも同期が・・・）。
JMeterを使って負荷テストをする方法（最小限）

# JMeterとは
 JMeterとは、パフォーマンス計測用のJavaアプリケーションです。元々はWebアプリケーションのテストのために作成されたもので、**さまざまなWebアプリケーションをテストする機能**を持っています。現在では、Webアプリケーション以外のさまざまなものに対してもテストできるように拡張されています。
![ok.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/6e96a36a-48d1-4aa9-04cf-620d32c8424c.jpeg)

# 導入
[Apache](http://jmeter.apache.org/download_jmeter.cgi)公式から、Apache JMeter 5.2.1 (Requires Java 8+)のBinariesからapache-jmeter-5.2.1.tgzもしくはapache-jmeter-5.2.1.zipをダウンロードします。(2019/12/31最新)
※Java8が存在する前提で進みます。存在しない場合は、ダウンロードしてからお進みください。


ダウンロードが完了したら、解凍を行い、apache-jmeter-5.2.1フォルダの中のbinフォルダの中のApacheJMeter.jarをダブルクリックして起動します。起動すると以下のような画面が表示されます。

<img width="1435" alt="スクリーンショット 2019-12-31 4.20.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/1da74d37-f404-f997-3edc-624dfb8c4c79.png">

# 使ってみる
### スレッドグループの作成
テストを行う上でスレッドグループというものを作成する必要があります。スレッドグループでは、HTTPリクエストをどのように、どれぐらいの量、どれぐらいの期間で送信するかを設定できます。
これらを決定するのが、スレッド数、Ramp-Up期間、ループ回数 です。

以下の画像のように**テスト計画**を右クリックし、**スレッドグループ**を追加します。
<img width="1440" alt="スクリーンショット 2019-12-31 4.24.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/5bfa8562-593f-d1cf-02f5-6e60e965d308.png">

スレッドグループでは大きく分けてスレッド数、Ramp-Up期間(秒)、ループ回数の3つの項目を設定可能です。

例えば、スレッド数100、Ramp-Up期間を100、ループ回数を1とすると、「100のスレッドを100秒かけて処理を行い、スレッド毎に1回だけ実行する」という意味になります。
スレッドを立ち上げる間隔というのは等しく生成されるので、100秒間に1秒に1回のリクエストが投げられるということになります。

<img width="1434" alt="スクリーンショット 2019-12-31 5.20.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/3ee67b5e-0b92-c484-d226-dca4c09acd8d.png">

### HTTPリクエストの作成
スレッド内で実行するテストの作成を行います。
以下の画像のように、**スレッドグループ**を右クリックし、**HTTPリクエスト**を追加します。
<img width="1432" alt="スクリーンショット 2019-12-31 4.33.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/e4a715ea-c915-2cd0-9cff-4707521967ea.png">

HTTPリクエストでは、負荷テストを行うURLやプロトコル、ポート番号、メソッドなどを任意で選択することができます。
今回は`?????????.com`にGETリクエストを送信するという内容のものを指定する。(悪意を持って第３者のWEBサーバに負荷をかけることは罪に問われる可能性がある。)

<img width="1428" alt="スクリーンショット 2019-12-31 4.42.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/8b9b0638-e584-306a-87ca-2a7ed70f22f2.png">

### リスナーの作成
負荷テストを行なった結果を確認するためにリスナーをスレッドグループに追加します。
以下の画像のように、**スレッドグループを右クリック**し、**結果を表で表示**
を追加します。

<img width="1437" alt="スクリーンショット 2019-12-31 4.48.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/5e5842df-3a62-51f6-50bb-09ba3d85c4a0.png">

## 負荷テスト実行
実行ボタンを押します。
結果を表で表示をみると、以下のように実行結果をみることができます。

<img width="1434" alt="スクリーンショット 2019-12-31 4.51.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395817/98d61119-3031-b477-f049-f360dd26ee9d.png">

# 最後に
今回は、とりあえず負荷テストを行うための最小限の機能だけを、JMeterで紹介しました。
JMeterには表以外にも、グラフで負荷を表示させたりといった様々な結果の見方があります。
是非、お試しください。


次回は、AWS EC2やS3にデプロイされているのに対して負荷試験を行なった中で得られた知見などを紹介していきたいと思います。
KubernetesのIngressで使うSSL自己署名証明書をPowerShellで作る

... 自己署名証明書を使うほうが最早難しい時代だよねメモその1。

EDIT: secretに秘密鍵と公開鍵を同じファイルで入れても動くけど、同じファイルなのは微妙に紛らわしいので注記。

# 証明書の作成

適当にPowerShellを開いて、 [`New-SelfSignedCertificate`](https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps) コマンドで作る。

```powershell
New-SelfSignedCertificate -DnsName "flow.local" -NotAfter (Get-Date).AddYears(50) `
 -CertStoreLocation "cert:\CurrentUser\My" -KeyLength 4096 -Type SSLServerAuthentication `
-TextExtension @("2.5.29.19 = {critical} {text}ca=1&pathlength=0") `
-KeyUsage CertSign,KeyEncipherment,DigitalSignature
```

`KeyLength` みたいなパラメタはお好みで良いけど、難しいのは `TextExtension` と `KeyUsage` パラメタで、それぞれ:

- `TextExtension` : このパラメタでは作成する証明書を **CAの証明書** に見せている。普通自己署名証明書はCAの証明書である必要は無いが、 [何故かOpenSSLはKeyUsageにCertSignが入っている場合CA証明書じゃないと証明書をRejectしてしまう](https://security.stackexchange.com/questions/143061/does-openssl-refuse-self-signed-certificates-without-basic-constraints) ようなので含めている。 `2.5.29.19` が [basicConstraints](http://oidref.com/2.5.29.19) の意味で、 `pathlength=0` はこの証明書がさらに下位のCAを持つことが無いということを示す。
- `KeyUsage` : デフォルトでは `KeyEncipherment,DigitalSignature` となるが、 `CertSign` を加えている

これで作成した証明書は、ファイル名を指定して実行 → `certmgr.msc` で見つけられる。

![SnapCrab_NoName_2019-12-31_3-48-9_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67446/a2aa043b-5c42-25b9-7de3-655182125a55.png)

管理ツール上の証明書は特に用途が無いので、インストールが終わったら消してしまって良い。

# 秘密鍵のインストール

管理ツールでの証明書を右クリックしてエクスポート...から秘密鍵をエクスポート、エクスポートされた `.pfx` ファイルに `openssl` コマンドを掛けて鍵データを `.pem` 形式で取り出す:

```
openssl pkcs12 -in flowlocal.pfx -nodes -out key.pem
```

(Windowsでは秘密鍵のエクスポート時にパスワードが必須になっている)

取り出した `.pem` を `kubectl` で secret としてインストールする:

```
kubectl create secret tls flowlocal-tls --cert=key.pem --key=key.pem
```

`cert` と `key` は同じファイルでOK(1つのファイルに両方を入れておける)。ここでは `flowlocal-tls` という名前で作成している。 EDIT: **この方法でTLS鍵として機能するが、公開鍵ファイルの中に秘密鍵も一緒に入ることになって微妙に気味が悪いので注意** 。 気になるなら `openssl` コマンドに `-nocerts` とか `-nokeys` を与えて別々のファイルにする。

# 秘密鍵のIngressでの使用

これ以降は普通のTLS鍵と違うところはない。単に TLS のsecretとして指定すれば、Ingressのトラフィックは自動的にその鍵で暗号化される:

- https://github.com/okuoku/nextstripe/blob/10a709178cf368f0fd9bf0b4dbbc3ad296e90964/ingress/00tls-local.yaml#L65

```yaml
    tls:
        - hosts:
            - flow.local
          secretName: flowlocal-tls
```

ただし、ここの `hosts` と `New-SelfSignedCertificate` に与えた `DnsName` は一致している必要がある。
初めてddでdisk backupをしたらパーティションとか関係なかった話

# fs壊れたVM、どーしようもなくなって、 xfs_repair しようとしたんだけどbackupしないと怖くね？って状態だったのでbackupの勉強した話

酔って書いてるよりも、もとの知識がアレなので非常にアレな記事です。

最初にconsole見たときは emergency modeになってた。/sysrootがmountできないというエラーで、ls /sysrootは空っぽでした。天才の同僚がrebootしたらrescue modeで起動できたと。

今回初めて知ったこと。centos 7 には grub 選ぶとこでe押したあとの systemd.unit=rescue.target ってのがあるらしい。
後で調べたらこれは（自分にとっては）ただのsingle user modeだった。rescue modeって名前には聞いてたんだけど、なんだ、ただのシングルユーザモードのことだったのか。

> rescue mode is equivalent to single user mode 
> [RHEL 10.3. WORKING WITH SYSTEMD TARGETS](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/sect-managing_services_with_systemd-targets)

それならMac OS 10.1の頃にadmin password忘れて通った道だよ。cmd+sで起動するやつでしょ？

昔話はさておき、ここは想像なんだけど rescue という名前・・・しかし所詮 /dev/sda とかで起動してるからこれってルートパーティションを xfs_repair できないよね？（誰か教えて） 
DVDとかpxeで起動する rescue と全然違くない？　それ同じ言葉でいいの？ 違う言葉あるの？

関係ないけど res**q**ue.target と打ってたせいでしばらく全然rescueできなかった。反省typo。

### 学んだこと
簡単に言うとemergency modeが root partitionをmountできないときにイケるやつ（たぶん /boot とか initramfs だけでなんとか起動するやつ）で、rescue.target は /def/sda で無理やり起動するやつ。だから /etc/fstab で root partition がやばい系だと rescue.target は行けないんじゃないかと思う。/dev/sdb がマウントできないとかならいけそう。（誰か合ってるか間違ってるか教えて）
いや待てよ、initramfsてdiskから読んでるんじゃないのか。あいつどこにいるんだっけ、、。

いい加減記事の本題に入る。

## 最初にやってコケた話
手元のvirtual boxにあるvmが30GBもdiskあった
とりあえず練習でこれcopyしてみっか、と思ってやったのは・・

どうせ0で埋めるだけだし8GBでよくね？という性格なもので、適当にvirtual boxデフォルト=8GBなdiskをVMに追加して、/dev/sdbにする。パーティション必要だよね、と思ってpartedで/dev/sdb1を作る。やっぱxfs？という、ここらへんはもう手癖でmkfs.xfsする。

ddコマンド叩くとこで、あれ？どこcopyするんだっけ？ os起動してるとこ？ /dev/sda1と/dev/sda2があって、sda2の下に大事そうなルートパーティション、 /dev/mapper/centos-root でいいの？あれ？　

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/9d7c95a2-01dd-5dde-0cbc-c2e500f28fd1.png)

これ絶対違うわー、根本から間違ったわー、でもこれも経験と思いながらとりあえず

dd if=/dev/mapper/centos-root of=/dev/sdb1 したら、macがめっちゃ重くなってしばらくしたら突然rebootしたｗ

## うまく行った話
素直に copyしやすい 8GB のdiskで、centos7をinstall。素直。
8GBの別diskをatouchしてrescue.target起動。/dev/sdbに見えることを確認。で、この単純明快コマンド。

    dd if=/dev/sda of=/dev/sdb

パーティションの準備とかいらないんです。物理ディスク領域？をまんまいくんです。copyは順調に終わり、いったんshutdown。もとのdiskをVMからdetouch。追加diskだけの状態にして起動。普通にos使える。複数パーティションまとめて全部完璧に複製されたってことです。すげー。これがddですか。すげー。最初の失敗から、何もぐぐらず想像でいけた自分に感謝。

いやー勉強になった。dd めっちゃ便利だな！！


## ddの進捗を見る方法
とりあえずまだがんばってるかはiotopで見れる。

```sh
yum install -y iotop strace
# -o で稼働中のものだけを表示
iotop -o
```
こんな漢字↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/42f237cc-73d1-8494-281e-ecc1be57656c.png)

straceだと書き込んでる内容が見える。ただ100GBだと100GB以上の文字列が流れるのでNW越しだとなかなかつらい。tail繋げるといいというのを見かけたが、自分の環境では蕩かなかった

```sh
ps aux | grep dd # pidを取得
strace -p 4458    # pidを引数に
strace -p $PID | tail -n 5         # last 5行だけ表示（自分はうまくいかず）
```
ひとり開発時の備忘録

#Firebase
- firebase login
- firebase init
- firebase deploy

#Bitbucket
[bitbucketのコマンド](https://qiita.com/yyosuke/items/986dabc9906674e2ea97)

- 初期化
    - git init
    - git remote add origin git@bitbucket.org:xxxx/xxxx.git

- アップロード
    - git add .
    - git commit -m "xxxx"
    - git push

- ダウンロード
    - git pull　

#時短開発
[vscodeでEmmet](https://technical-creator.com/vscode-emmet/)
html:5,h1#id,h1.class,ul>li*3 などは覚えておくと便利

[vscodeでホットリロード](https://www.nxworld.net/services-resource/vscode-extension-live-server.html)

#ジェネレーター開発
[canvasで図形描画と画像保存](https://www.otwo.jp/blog/canvas-drawing/)
[canvasで文字](http://www.htmq.com/canvas/fillText.shtml)
[]()
[]()
[]()
[]()
[]()
QiitaをTwitterみたいに使うのって運営的にはどう思っているんだろう。

Qiitaに140文字ぐらいの技術系記事を投稿しまくって、QiitaをTwitterみたく使ったら楽しいんじゃない？
TwitterってCode載っけても見にくいし、QiitaならCode載っけるときれいに見えるし。
フォロー機能とかあしブログの宣伝記事とかもいっぱいあるわけだからTwitterみたく使ってもいいのかな？
Slackに2chチャンネルを作るときこそ、Goolge Apps Scriptを使うべきだと思う。

[社内slackにVIPチャンネルを作った話] (https://qiita.com/peisuke/items/80984db8b47cd8243019)

という記事が一ヶ月前ぐらいにQiitaで流行っていたが、AWSとかよくわからない人はGoogle Apps Script(GAS)で作ると良いと思う。
GASならクレカの登録無しかつ無料で同じようなものがかなり簡単に実現出来る。

参考として

[Slash CommandsとGASでSlackのオリジナルコマンドをつくる] (https://qiita.com/chikuwa111/items/7a1a349b82318a5861cc)

この辺の記事を読むといいと思う。
【CSS】背景画像を複数枚重ねてレスポンシブに対応させる

Webページの背景画像で、一部の画像だけ形を崩さずにディスプレイサイズに対応させる方法です。

##やりたかったこと
<img width="525" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559543/089f7625-c102-5796-67f7-99e99a4b4abe.png">
最初はこのような1枚画像を用意して、ブラウザサイズに合わせて画像を変えようとしました。
が、ディスプレイサイズに合わせて縮小させると波線形状部分の形が崩れるし、画像固定にしてしまうと手前の波線画像の一部しか見えず型崩れ（あるいは画面の８割を締める）・・・など問題が出て来たので、手前の波線形状の画像だけ、常に形を崩さず最下部に表示できるにしました。

###例
<img width="183" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559543/b95efe9a-20d2-ffcc-17b6-0d06c21809f5.png"><img width="130" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559543/f670b7ad-6468-4a36-f163-4bd47b83ea30.png">

ディスプレイの横幅が変わっても、波線形状だけは形を崩さず、画面下部にいるようにします。

##やり方
一番奥の背景画像(desktop1.jpg)、手前の波線画像(cover1.png)をそれぞれに用意。

        body{
        background: url(./img/cover1.png),url(./img/desktop1.jpg);
        background-repeat: no-repeat,no-repeat;
        background-position: bottom,top;
        background-size: contain,cover;
        background-attachment: fixed,fixed;
        }

background-sizeを背景画像はcoverにし、隙間が生まれないように。
波線画像はcontainにすることで、常に画像の全体が見えるようにしました。
WordPressでブログを作りたいと思ったら、Google Cloud PlatformのCompute Engineで作ると良いよ。なぜなら、無料だから。


1. docker環境の構築
2. リバースプロキシをdockerで立てる
3. wordpressのdockerを立てる。
4. wordpressのdockerを立てる。


こんな感じでいっぱいwordpressのブログを無料で作れる。
コツとしては、swap領域をきちんと確保しておくと良いよ。
そうしないとwordpressのdockerが起動できないから。

ちなみにドメインは別料金なんで、年間1000円ぐらいかかる。
GCPの固定IPが来年から有料化するっぽいから来年からどうなるんだろう。

以下の記事に処理がおそすぎて使い物にならないと書いてあるが、私の環境ではきちんと使えている。
https://qiita.com/rache/items/3a2825a2d848b903b03b

やっぱり、真面目にやるなら月500円ぐらい払ったほうがいいかもしれない。
Twitterで登壇資料をサルベージする

# はじめに

現在、勉強会などのイベント資料が```SpeakerDeck```や```SlideShare```上に配布され、Twitter上でハッシュタグ付きで共有されています。ただし、登壇者によっては、配布場所は異なるため、これらの資料を検索する方法について検討した内容を共有します。

# Twitterの検索機能を活用する

* ツイートに含まれるURLを検索する。
    * ```例) url:slideshare.net```
* 複数サイトのURLをOR検索する。
    * ```例) (url:sliedehare.net OR url:speackerdeck.com)```
* ツイートに含まれるハッシュタグを検索する。
    * ```例) (#builderscon)```
* イベントの開始期間を指定する
    * ```例) since:2019-08-29```
* イベントの終了期間を指定する
    * ```例) until:2019-08-31```

# イベント資料の配布URL

ツイートで配布されている主な配布箇所一覧です。

|配布箇所  |配布URL ※ツイート含まれるURL  |
|---|---|
|SlideShare  |[slideshare.net](https://slideshare.net)|
|Speaker Deck   |[speakerdeck.com](https://speakerdeck.com)  |
|Google Drive|[drive.google.com](https://drive.google.com)|
|Dropbox|[dropbox.com](https://dropbox.com)|
|iCloud|[icloud.com/keynote/](https://icloud.com/keynote)|
|SlideLive|[slidelive.jp](https://slidelive.jp)|
|Google Docs|[docs.google.com](https:docs.google.com)|
|Adobe XD|[xd.adobe.com/view](https://xd.adobe.com/view)|
|slides|[slides.com](https://slides.com)|

# 検索例

2019年8月29日から2019年8月31日まで開催された```builderscon tokyo 2019```のツイートを対象に検索した例です。ハッシュタグは [#builderscon](https://twitter.com/search?q=%23builderscon&src=hashtag_click) です。


[(
 url:www.icloud.com/keynote/ OR
 url:slideshare.net OR
 url:speakerdeck.com OR
 url:dropbox.com OR
 url:slidelive.jp OR
 url:docs.google.com OR
 url:xd.adobe.com/view OR
 url:slides.com
)
since:2019-08-29 until:2019-08-31
(#builderscon)](https://twitter.com/search?q=(%20%20url%3Awww.icloud.com%2Fkeynote%2F%20OR%20%20url%3Aslideshare.net%20OR%20%20url%3Aspeakerdeck.com%20OR%20%20url%3Adropbox.com%20OR%20%20url%3Aslidelive.jp%20OR%20%20url%3Adocs.google.com%20OR%20%20url%3Axd.adobe.com%2Fview%20OR%20%20url%3Aslides.com%20)%20since%3A2019-08-29%20until%3A2019-08-31%20(%23builderscon)&src=typed_query)


# さいごに

実際に色々と検索したみると、上記の配布箇所以外に noteやQiitaなどで資料が配布されているケースがあるかと思います。その際は、配布URLを調整することで検索できます。
また、min_favesなどの別の条件を追加して検索してみるのも良いかと思います。
学科内のdocker勉強会に登壇した話

# 経緯
私が通っている大学では3回生の後期からプレゼミという形で研究室に所属することになります。私は希望していた自然言語処理研究室に無事配属が決まりました。
そんな研究室の院生から「docker使いたいから誰か勉強しておいて」というお達しが出ました。夏休みに長期インターンシップでdockerを使っている企業に行っていたこともあって、手を上げました。後日、「勉強会したいから、スライドとか作っといて」と言われ、登壇することになりました。

学科内の勉強会に登壇していくつかの気付きがあったので、備忘録とともに、同じ境遇の人の参考になればと思い、ここに記します。

# 要件
登壇時間:1時間半
受講者数:60人程度
受講者内訳:学部3年生が50人、学部4年生が5人、院生5人
受講者のPC:殆どがwindows10home、一部Macbook
docker toolbox:全員未インストール

#登壇環境
j308
プロジェクターでっかいの
(HDMI端子がないため変換ケーブル持ち込み推奨)
Ethernet
学内wifi
# 講義の流れ
1. 自己紹介
2. docker toolboxインストール
3. dockerの紹介
4. 演習
5. まとめ


# 意識したこと
## スライドのポイント数
教室は広めなのでフォントサイズを最低でも32ptにした。
## スピード
自分が思ってるよりゆっくり話す必要がありそうです。
自分は理解しているため、話す速度が早くなりがちです。受講者が理解できる速度で説明を行うべきだと感じました。
演習では、半数以上が出来ていることをその都度確認し、ゆっくり進んでいきました。
# 至らなかった点
## 共有スペース
インストール時などURLの配布がうまく行かなかったです。QRコードを用意し工夫しましたが、結局はURLをタイプしている人が多かったと思います。
事前に教員や院生に協力をお願いし、受講者を把握しslackのチャンネルなど用意しておけばスムーズだと感じました。
## フィードバック
初めて勉強会に登壇し、うまく伝えられたか、自分で考えうること以外に改善点はないか。学内で勉強会を行っているので、googleフォームなど用意して、フィードバックをもらっても良かったのではないかと考えています。
## スライドの比率
16:9でスライド作成しましたが、多くのプロジェクタは4:3のようです。
16:9で作成するとスライドタイトルがほぼ切れていました。残念でした…
# 最後に
勉強会に登壇することは最速で学習するには非常に良い手段だと感じました。
アウトプットを行うことで理解が曖昧だった部分が明確になるんだと思います。
アウトプットの重要性を感じました。
また、次回、勉強会に登壇する際には今回の反省点を踏まえて挑戦したいです。
あと、うまくqiitaの記事かけるようになりたいです。
MariaDBのポート番号を変更する

LinuxのサーバーにインストールしたMariaDBについてポート番号の変更方法を下記に記す。

# サービス停止
設定を変更する前にサービスを停止する。

```
$ sudo systemctl stop mariadb
```

# 設定ファイルを変更
念のため設定ファイルはバックアップファイルを作成することを推奨する。

```
$ sudo cp /etc/my.cnf.d/server.conf /etc/my.conf.d/server_bk.conf
```
設定ファイルを開く

```
$ sudo vi /etc/my.cnf.d/server.cnf
```

開いたファイルの末尾にポート番号指定の一文を追記する。

```
port=任意ポート番号
```

# サービス起動
ポートを指定したあと、サービスを起動する。

```
$ sudo systemctl start mariadb
```

# ポート設定確認
MariaDBにログインして下記のSQLで確認する。
[3306]の部分が指定した任意ポート番号になっているとOK

```
mysql > show variables like 'port'

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| port          | 3306  |
+---------------+-------+

```

以上
【Rails】progateばっかりやってないで、簡単なアプリ作ったほうがいい話

初学者あるあるだと思いますが、progateばかりやりがちで、いざ何かを作ろうと思っても何もできないということがあると思います。（そんなことないよという方はサッとこの記事を読むのをやめてください。）

とかく、5年前の自分もそんな感じでした。Rails tutorial終わって蓋を開けてみれば何も作れるようにはなっていない。それどころか、あまり理解もできていないという状態でした。

そんな状態で、僕がやったのが超絶的に簡単なアプリを作ることでした。CRUD処理ができるだけのアプリを作ってみることで、Rails TutorialやProgateで学習する何十倍もの効果あると思っています。

当時僕が作ったアプリの概要を載せておきますので、ProgateやRails Tutorialをやってみたがあまり理解できていないと思った初学書の方、ぜひ一度以下のアプリを作ってみてください。

わからないことはTwitterにて質問していただけたらお答えします。

## 概要
アプリ名：　EasyBlog

*  本アプリの機能については機能一覧をご覧ください。
* データベースについてはDB設計をご覧ください
* ページデザインについては、ページデザインをご覧ください

scaffoldは使用しないでください。

## 機能一覧
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116887/f368b27c-ef7e-bd65-1312-977c6f327e92.png)


## DB設計
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116887/cf586f86-5c18-a144-4f47-ef52d540a7f9.png)


## ページデザイン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116887/5549adbd-6c23-3ea8-9723-a285e78bb662.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116887/39af1196-dc18-cd53-c936-95e8fc69e190.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116887/613103cc-6eb1-c321-22be-9c0f12f4d102.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116887/7efc0e2b-3de4-54bc-f4f1-b1b7704c93f3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116887/1b6ee342-6a6b-4463-74ac-be35255de796.png)
puppeteer実行した時に『アプリケーション“Chromium.app”へのネットワーク受信接続を許可しますか?』が表示されるときの解決策

# [2023年8月28日追記]
参照元のblogが削除されていたため、再度方法を調べ以下にまとめました。
https://zenn.dev/ohran/articles/7b1f3cbf9db6a5

# [2023年8月28日追記ここまで]


自分用の備忘録
下記ブログのとおり。

[リンク](https://www.w-dp.com/bokumemo/accept_incomming_connectionsdialog/)
react-final-formでenter押下時にpreventDefaultする


# example
```react
<Field name="myInput">
  {({input}) => (
    <input
      {...input}
      id="myInput"
      onKeyPress={e => {
        if (e.key === 'Enter') {
          e.preventDefault()
          ...
        }
      }}
    />
  )}
</Field>
```

# メモ
引数のinputがどうなってるのかいまいちわかってない:sweat_smile:
さくらのVPSにMariaDBをインストール

さくらのVPSの申込みが完了して一発目にMariaDBをインストールするまでの備忘録

# リポジトリを指定
MariaDBのリポジトリを下記のコマンドで指定します。

```
$ curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash
```

# MariaDBをインストール
下記の手順に従ってインストールを実施。

## インストール

```
$ sudo yum install MariaDB-server MariaDB-client
```

## MariaDBのサービスを開始

```
$ sudo systemctl start mariadb
```

## サーバー起動時にサービスも自動起動するようにする

```
$ sudo systemctl enable mariadb
```

## MariaDBの初期設定

```
$ mysql_secure_installation
```
## 設定ファイルの場所
MariaDBの設定ファイルは下記のディレクトリにあります。

```
/etc/my.conf.d
```

## ちなみに
セキュリティ上、ポート番号変えたりユーザー権限変えたり、要件によって追加の設定はした方がいいと思います。
Grove - Barometer Sensor (BMP280) をRaspberry Pi Zeroで使う

# やること
相対的な高さを定期的に取得する機能を実現したい。
Barometer Sensor (BMP280)をラズベリーパイZero用ベースハットに接続して動かす。

# 手順
残念ながらSeeedのWikiやGrovePiのサンプルにはなさそう。
更に調べたところ、このソースがそのまま使えそうなので試す。
https://github.com/pimoroni/bmp280-python

まずは、I2Cの端子に、BMP280を接続する。
その後以下を実行。

``` sh
sudo pip3 install bmp280
git clone https://github.com/pimoroni/bmp280-python
cd bmp280-python/examples/
sudo python3 relative-altitude.py 
```
するとI2Cのアドレスが間違っている（Groveのセンサーのアドレスはデフォルト０x７７なのに対して、スクリプトのデフォルトは、０x７６になっている）ので、

```py
bmp280 = BMP280(i2c_addr=0x77, i2c_dev=bus)
```

として再度実行すると動作した。
以下のように、センサーをもって軽く上げ下げするだけでも値の変化を確認できたので一旦満足。

```
Relative altitude: 01.04 metres
Relative altitude: 01.08 metres
Relative altitude: 01.23 metres
Relative altitude: 01.30 metres
Relative altitude: 01.37 metres
Relative altitude: 01.45 metres
Relative altitude: 01.53 metres
Relative altitude: 01.54 metres
Relative altitude: 01.59 metres
Relative altitude: 01.64 metres
Relative altitude: 01.67 metres
Relative altitude: 01.74 metres
Relative altitude: 01.75 metres
Relative altitude: 01.75 metres
Relative altitude: 01.50 metres
Relative altitude: 01.27 metres
Relative altitude: 01.17 metres
Relative altitude: 01.11 metres
```
Vim風の矢印をhhkbで導入する。　KeySwapを使ってみた

#前書き
12月の上旬にhhkb professional hybrid type-Sが発売されました。Bluetoothの改善、キーの静穏化、キー配列の変更機能がキーボード本体に保存できるなどかなりパワーアップした製品が発表され盛り上がった方も多いのではないでしょうか？
このなかでも自分のなかではキーボードの配列の変更機能が非常に素晴らしいと思いました。しかし、私は今年の9月にhhkb professional jpを買ったばかりなのでさすがに買いなおすのは正直きついなあと思いました。しかし、このことでキーの配列を入れ替えるという発想を得たので他の方法でキーの配列を入れ替えたいと考えました。

#やりたいこと
vimの操作に慣れてきたので矢印キーをhjkl+Fnに割り当てたいと考えました。

#環境について
windows10
keyswap(キー配列を変えるフリーソフト）
hhkb professional jp
#Keyswapのインストール
https://www.vector.co.jp/soft/winnt/util/se228667.html からダウンロードできます。
1.ダウンロードしたファイルを開き、すべて展開を選択します。
2.展開した後、右クリックで管理者として実行を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455763/2561bd7a-807f-ce95-b1c2-548846ca6cc4.png)

#keyswapの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455763/ac462eb8-0f4b-ce43-5a2f-da67f6512fa5.png)
管理者として実行をすると以下の画面が表示されます。
キー名の方に元のキー変換キー名のほうに変更したいキーをクリックして入力し、ファイルに保存して終了をクリック、コンピュータの**再起動**をすることでキー配列を変更が適応されます。
複数のキーをつかったものは登録できませんが、**Fnキーを使って押せる*や/はテンキーの方を選択することで選べるようです。**
とりあえず、Fn + h,j,k,lは全く使っていなかったので他に割り当てずに変更しました。

#気になること
私は俗にいう尊師スタイルとよばれる、リチャードストールマン発祥（？）のノートパソコンの上にhhkbを載せて使うこともあります。hhkbを載せているときはいいのですが、ノートパソコン単体で使う際にはキー配列が違うのでノート単体でも使う方はその都度設定を変更しなければなりません。
また、Fnキーが若干遠いで割り当てを変更しようか検討中です。

#あとがき
vimになれるにはまず矢印からといいます。vimに慣れたい、もしくは慣れてしまった方はこの設定をすることでvimの操作になれる、ストレスなく操作できるようになると思います。
HHKBの新作も欲しいですが、自分の"鞍"はできるだけ変えずに生きていきたいところですね。
rustup を非対話的環境でインストールする方法

# TL;DR

rustupのインストールを非対話環境でインストールするには、以下のコマンドを実行する

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
```

# 動機および調査ログ

Rust をインストールするにあたって、[rustup.rs - The Rust toolchain installer](https://rustup.rs/)にあるコマンドをコピペして実行することが定石だと思います。

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```

これを実行すると、以下のような表示が出て、ユーザーの入力待ち状態となります。

```
info: downloading installer

Welcome to Rust!

----中略----

Current installation options:


   default host triple: x86_64-unknown-linux-gnu
     default toolchain: stable
               profile: default
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>

```

通常の環境であればここで1などを入力すれば良いのですが、そういうわけにはいかない環境ではどのようにすればいいのか。
[curl でダウンロードした対話式シェルスクリプトが bash にパイプ渡しできない - Qiita](https://qiita.com/KEINOS/items/98164eb05290da7b9d35)
を参考にして

```bash
yes | sh <(curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs)
```

でいけないかと試してみましたが、ダメでした。

解決策はこちらにありました。
[Non-interactive install · Issue #83 · rust-lang-deprecated/rustup.sh](https://github.com/rust-lang-deprecated/rustup.sh/issues/83)

`-s` と `--` については [Man page of BASH](https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html) を確認すると以下のように書いてあります。

> -s オプションが指定された場合と、 オプションを全て処理した後に引き数が残っていなかった場合には、 コマンドは標準入力から読み込まれます。 このオプションを使うと、 対話的シェルを起動するときに 位置パラメータを設定できます。

> -- はオプションの終わりを示し、それ以降のオプション処理を行いません。 -- 以降の引き数は全て、ファイル名や引き数として扱われます。 引き数 - は -- と同じです。

そんなわけで、結果的に冒頭に書いたコマンド（以下再掲）を実行すればOKです。

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
```
量子コンピュータ

今回は諸知識の投稿を進めていこうと思います。


# 量子コンピュータとは

平たく言いすぎると、すごい性能のコンピューター

もっと詳しくいうなら、今までコンピューター(古典コンピューター)では
0か1の状態しかあらわせなかったのが、2や3,4・・・など
表すことができるようになり処理が格段に変わってくることになります。

# 量子コンピューターの期待

色々意見がありますが、知る限りはこの2点が注目されてます。

## モビリティ分野

量子コンピューターは、電気自動車などを利用したMaaS向け車両の円滑な運用や、交通データの最適化による交通問題の解決に向けて活用される可能性があります。

### MaaS
EV(電気自動車)を利用したMaaS(Mobility as a Service)で
MaaSとは、カーシェリングをはじめとしたシェアリングサービスや、様々な交通手段の利用に際してスマホから検索・予約・支払いを一度に行えるサービスの実施などの新しい取り組み。

移動の効率化、都市部での渋滞や環境問題、交通弱者対策などの問題解決を目的としている。

## 金融ビジネス分野
◆ ポートフォリオ最適化
複数の投資銘柄の中から最適な組み合わせを選ぶこと。

◆ 将来株価予測・株価変動予測

◆金融商品の価格決定

◆リスク管理業務におけるシミュレーション

# 量子コンピューターでできること
量子コンピューターの強みを活かしてできること・実用化が望まれていることはこちらです。

## 画像認識への応用

量子コンピューターの量子ビットの性質を利用することで
この画像認識を識別する精度がさらに高くなると言われています。

## 堅牢なセキュリティ

耐量子コンピューター暗号という、量子コンピューターに解けない暗号化技術の開発も進んでいます。

同時に、量子コンピューターをセキュリティ面にも活かそうという試みから
量子テレポーテーションと呼ばれる技術の研究も盛んです。

### 量子テレポーテーション
量子テレポーテーションとは、超高速通信を可能にし
理論上、情報漏洩や盗聴ができないセキュリティを実現できると言われています。

## 組み合わせ最適化

莫大なデータの選択肢から計算を通じて最適なものを抽出することを組み合わせ最適化といいますが
これを実施するためにQAOA(Quantum Approximate Optimization Algorithm)という
量子アルゴリズムと量子アニーリングという技術が注目されています。

組み合わせ最適化による業務・生産の効率化が実現すれば
これまでの負荷やコストの軽減を期待することができ
物流の配送ルート探索、新薬開発時の分子構造の決定
収益率が高い金融ポートフォリオの作成など広い分野に有効だと言われています。
【GCP】Google Cloud StorageをPythonで操作する

#google-cloud-storageのインストール
```shell
$ pip install --upgrade google-cloud-storage
```

#ストレージクライアントの作成
```python

from google.cloud import storage
client = storage.Client()
```

#バケットに対する操作
###バケットの作成
```python
bucket = storage.Bucket(client)
bucket.name = "test-bucket"
bucket.location = "asia-northeast1"
client.create_bucket(bucket)
```

###バケットの取得
```python
bucket_name = "test-bucket"
bucket = client.get_bucket(bucket_name)
```

###バケットの一覧表示
```python
for bucket in client.list_buckets():
    print(bucket.name)

# test-bucket1
# test-bucket2
# test-bucket3
```

###バケットの存在チェック
```python
print(bucket.exists())

# True
```

###バケットの削除
```python
bucket.delete(force=True)
```
バケットに対して削除リクエストを送信する場合、バケットは空である必要があります。
パラメータ「force」をTrueにすることで、バケットのオブジェクトを全て削除した上でバケットを削除できます。(Trueで空にする、デフォルトはFalse)
ただし、バケット内のオブジェクトが256個を超える場合、ValueErrorが発生するため、注意が必要です。

#バケット内のオブジェクトに対する操作
###オブジェクトを一覧表示
```python
for blob in client.list_blobs(bucket_name):
    print(blob.name)

# test_dir/
# test_dir/hoge.txt
# test_dir/test_file_1.txt
# test_dir/test_file_2.txt
# test_file_1.txt
# test_file_2.txt
```

###オブジェクトを一覧表示(Prefix指定)
```python
for blob in client.list_blobs(bucket_name, prefix="test_dir/test"):
    print(blob.name)

# test_dir/test_file_1.txt
# test_dir/test_file_2.txt
```

###オブジェクトのインスタンスを作成
```python
blob = bucket.blob("test_dir/test_file_1.txt") # ストレージのパスを指定
```
###オブジェクトの存在チェック
```python
print(blob.exists())

# True
```

###オブジェクトのダウンロード
```python
blob.download_to_filename("test_file_1.txt") # ダウンロード先のパスを指定
```

###オブジェクトのアップロード
```python
blob.upload_from_filename("test_file_1.txt") # アップロード元のパスを指定
```

###オブジェクトの削除
```python
blob.delete()
```


#参考
google-cloud-storage Library Reference
https://googleapis.dev/python/storage/latest/client.html
MySQLに世界地図をインポートする

[English page](https://github.com/hajime-miyauchi/shp2mysql/wiki/Import-world-map-into-MySQL)

# はじめに

結果はこんな感じ。

![スクリーンショット 2019-12-31 0.06.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/518413d9-7205-8355-8199-7b3dc0ed118c.png)

# 概要

OS: CentOS Linux release 7.6.1810
MySQL: 8.0.16

ツール([shp2mysql](https://github.com/hajime-miyauchi/shp2mysql))をインストールします。

```
$ wget https://github.com/hajime-miyauchi/shp2mysql/releases/download/v0.2/shp2mysql-0.2.tar.gz
$ tar xvfz shp2mysql-0.2.tar.gz
$ cd shp2mysql
$ ./configure
$ make
$ sudo make install
```

データを準備します。データは[Natural Earth](https://www.naturalearthdata.com/)からダウンロードします。いくつか種類がありますが、今回は[1:110m Cultural Vectors] (https://www.naturalearthdata.com/downloads/110m-cultural-vectors/)を使います。

```
$ wget https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/cultural/ne_110m_admin_0_countries.zip
$ unzip ne_110m_admin_0_countries.zip
```

インポートするデータベース(ここでは例としてgeotest)を作成します。

```
$ mysql -u root -p
mysql> CREATE DATABASE geotest;
```

データを変換してインポートを実行します。

```
$ shp2mysql ne_110m_admin_0_countries.shp > ne_110m_admin_0_countries.sql
$ mysql -u root -p geotest < ne_110m_admin_0_countries.sql
```

結果は下記SQLで確認できます。

```SQL
SELECT * FROM geotest.ne_110m_admin_0_countries;
```

# この記事のライセンス
[![クリエイティブ・コモンズ・ライセンス](https://i.creativecommons.org/l/by/4.0/88x31.png)](http://creativecommons.org/licenses/by/4.0/deed.ja)
この記事は[CC BY 4.0（クリエイティブ・コモンズ 表示 4.0 国際 ライセンス）](http://creativecommons.org/licenses/by/4.0/deed.ja)の元で公開します。
scikit-learnのMultiLabelBinarizerで個々の購入データを表にまとめる

# データ元

https://drive.google.com/file/d/1y5DYn0dGoSbC22xowBq2d4po6h1JxcTQ/view?usp=sharing

# 個々の購入データ（変換前）

<img width="230" alt="スクリーンショット 2019-12-31 00.13.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233208/8d9cb4aa-3d95-3e1f-695d-2774f5ff2935.png">

各行はユーザID、各列はユーザの購入したアイテムを表している。列数は最も購入アイテムの多いユーザの購入数になる。そのため、空欄にはNaNが入っている。

# つくりたいテーブル（変換後）

<img width="536" alt="スクリーンショット 2019-12-31 00.18.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233208/69e522d6-16a1-92d7-646e-924964543691.png">

各列を特定のアイテムに固定して、それぞれのユーザが購入したかを1/0で表す

# やりかた

scikit-learn の MultiLabelBinarizerを用いる。変換前のデータフレームをdfとする。変換後の出力はdf_transとする。

```Python
from sklearn.preprocessing import MultiLabelBinarizer

df = df.fillna("none")

mlb = MultiLabelBinarizer()
result = mlb.fit_transform(df.values)
df_trans = pd.DataFrame(result, columns = mlb.classes_).drop('none', axis=1)
```

データフレーム中にNaNが入っているとMultiLabelBinarizerでエラーが発生するので適当な文字列に変換しておく（重複しなければ別にnoneじゃなくてもよい）

MultiLabelBinarizerオブジェクトを生成しfit_transformメソッドを呼び出す。引数にはdf.valuesとしてnumpy.array形式で指定する。

列名（アイテム名）はmlb.classes_で取り出せる。

最後にdropメソッドでnoneを削除すれば、変換後のテーブルを得る。
EPS を PGF ソースに変換する Docker コンテナを作る

昨年，[ラムダノート](https://www.lambdanote.com/)の鹿野さんが，[TeXConf 2018](https://texconf2018.tumblr.com/) および [TeX & LaTeX Advent Calendar 2018](https://adventar.org/calendars/3382) の[4日目の記事](https://golden-lucky.hatenablog.com/entry/2018/12/04/183000)で，モダンな TeX 環境における EPS ファイルの活用法について述べておられました。

* [2018年でもEPSファイルをTeXで使う - golden-luckyの日記](https://golden-lucky.hatenablog.com/entry/2018/12/04/183000)

この記事中で，**eps2pgf** というツールが紹介されていました。

> 同じことを考える人はいるもので、すでに**eps2pgf**というJava製の変換ツールを作っている人が過去にいました（[参考](https://sourceforge.net/projects/eps2pgf/)）。 現在はSourceforgeにポストされているもののソースもない状態で、メンテもされてなさそうなんですが、とりあえずダウンロードして使ってみるとそれなりに満足のいく結果が得られました。

この [eps2pgf](https://sourceforge.net/projects/eps2pgf/) というツールは，10年以上前の Java 製ツール（更新日付を見ると2008年8月でした）ですが，現在は `Status: Abandoned` となっており，開発は長らく停止しているようです。Apache ライセンスではありますがソースは行方不明です。

ただ，今でも結構使えるありがたいツールではあります（EPS を PGF ソースに変換できると何が嬉しいのかは[鹿野さんの記事](https://golden-lucky.hatenablog.com/entry/2018/12/04/183000)を参照）。とはいえこのためだけに JVM をインストールするのも面倒なので，JVM とこのツールをパッキングした Docker コンテナを作ってみました。

# Java の Docker イメージとしてどれを選ぶか？

Docker Hub には，Java (JRE, JDK) のイメージが公式なやつだけでもたくさんあり，どれを選べばよいか迷ってしまいます。次の記事にそれぞれの特徴や選択基準がわかりやすくまとめられていました。

* [最適な Java の Docker イメージを選びたい - k11i.biz](https://k11i.biz/blog/2018/05/17/base-docker-images-for-java/)

今回は，

* 10年以上前の Java 製ツールを動かしたいだけであるから，Java のバージョンは最新でなくて問題ない。
* JDK は不要，JRE だけでOK。
* だからできるだけサイズが小さいものを選びたい。

というわけで，

* Alpine Linux ベース
* OpenJDK
* Java 8
* JRE のみ
* ファイルサイズは 84.9 MB（2019年12月現在）と軽量

である `openjdk:8-jre-alpine` を選びました。

# 使い方

完成したイメージは Docker Hub に [doratex/eps2pgf](https://hub.docker.com/repository/docker/doratex/eps2pgf) としてアップロードしてあるので，

```sh
$ docker pull doratex/eps2pgf
```

とすれば pull できます。

コンテナ内では `/workdir` が作業ディレクトリとなるので，それをカレントディレクトリと bind マウントして使うのを想定しています。

```bash
alias eps2pgf="docker run --rm \
  --mount type=bind,src=\"\$(pwd)\",dst=/workdir \
  doratex/eps2pgf"
```

などとエイリアスを設定しておくと楽でしょう。Docker イメージの `ENTRYPOINT` として `eps2pgf.jar` のラッパースクリプトを指定してありますので，このエイリアスが設定されている下で

```sh
$ eps2pgf tiger.eps -o tiger.pgf
```

などとすれば，EPS 図版を PGF ソースに変換できます。

# マニュアル

オリジナルの `eps2pgf.jar` のマニュアルは，[SourceForge のレポジトリのアーカイブ](https://sourceforge.net/projects/eps2pgf/)から入手できます。Docker コンテナ内の `/eps2pgf/doc/` にも収録されていますので，

```sh
$ docker run --rm \
  --mount type=bind,src="$(pwd)",dst=/workdir \
  --entrypoint /bin/cp \
  doratex/eps2pgf \
  -p /eps2pgf/doc/eps2pgf_manual.pdf .
```

などとしてカレントディレクトリに取り出すこともできます。


# Dockerfile

このイメージを作成するために用意した Dockerfile は以下の通りです。

```dockerfile:Dockerfile
FROM openjdk:8-jre-alpine

LABEL maintainer="doraTeX <taylorkgb [at] gmail.com>"

WORKDIR /workdir

RUN apk upgrade --update && \
    mkdir /eps2pgf && \
    cd /eps2pgf && \
    wget -q https://excellmedia.dl.sourceforge.net/project/eps2pgf/Eps2pgf/Eps2pgf%200.7.0/eps2pgf-0.7.0.zip && \
    unzip eps2pgf-0.7.0.zip && \
    rm eps2pgf-0.7.0.zip && \
    cd /usr/local/bin && \
    printf "%s\n" \
      "#!/bin/sh" \
      "java -jar /eps2pgf/eps2pgf.jar \"\$@\"" \
      > eps2pgf && \
    chmod +x eps2pgf

ENTRYPOINT ["eps2pgf"]
```
MacでQEMUを使ってRaspberry Piの仮想環境を構築する方法


<img width="682" alt="raspbian_on_qemu.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/e9a981bc-d22b-6e5c-c590-4998643b76c8.png">

# MacでRaspberry Piの仮想環境を構築
　Raspberry Piが手元になくて、Mac上で仮想環境を構築してみたときのメモです。

　最初に書いておくと、本当はGUI環境を構築したかったのですができなかったものになります。VNCでも良かったのですが、できませんでした…できるようになったら追記します（もしくは誰か教えて下さい）。なので、どれだけ役に立つかは微妙ですが、誰かの役に立つかもしれないので残しておきます。

　大まかな手順は以下です。

- QEMUをインストール
- 必要なファイル（OSイメージ等）をダウンロード
- QEMUでRaspianを起動

 順に説明していきます。

## QEMUをインストール
　QEMUというプロセッサエミュレータをインストールします。ARMのフリーの仮想環境といえばこれのようです。Macならパッケージ管理ソフトのHomebrewでインストールできます。具体的には、ターミナルから以下コマンド一発でインストールできます。

```sh
$ brew install qemu
```

　Homebrewって何？という人は以下記事参照下さい。
[Macのパッケージ管理ソフトウェア 「Homebrew」「Homebrew Cask」でソフトウェアを簡単にインストール・管理しよう](https://karaage.hatenadiary.jp/entry/2016/05/13/073000)

　インストールの確認は以下コマンドでできます。

```sh
$ qemu-system-arm --version
```

　以下のようにバージョンが表示されたらOKです。

```
QEMU emulator version 4.0.0
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers
```

## 必要なファイル（OSイメージ等）をダウンロード
　必要なファイルをダウンロードします。この辺りは、参考サイトに大いに助けられました。

　とりあえず、以下コマンドで必要なものが全てダウンロードできるようにしてみました。OSのイメージがあるので、かなり時間かかります（1GB以上のデータのダウンロードが必要）。

```sh
$ cd && git clone https://github.com/karaage0703/raspbian_on_qemu && cd raspbian_on_qemu
$ ./setup_raspbian_buster_on_qemu.sh
```

## QEMUでRaspianを起動
　以下コマンドでQEMUでRaspbianを起動します。

　無事成功すると、以下のようにログイン画面が表示されます。

ユーザー名:pi パスワード:raspberry でログインできます。

```sh
$ cd ~/raspbian_on_qemu
$ qemu-system-arm -kernel kernel-qemu-4.19.50-buster -dtb versatile-pb.dtb -m 256 -M versatilepb -cpu arm1176 -serial stdio -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" -drive file=2019-09-26-raspbian-buster.img,format=raw -no-reboot
```


　以下ログイン画面
<img width="682" alt="raspbian_on_qemu.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/e9a981bc-d22b-6e5c-c590-4998643b76c8.png">

# Docker上のUbuntuでQEMUの仮想環境
　GUI環境を諦め切れず、Docker上のUbuntuでもチャレンジしてみましたが、ダメでした。

　以下参照下さい（詳細の説明は省略します）。

https://github.com/karaage0703/Dockerfiles/tree/master/raspbian_on_qemu

　Dockerに関しては以下記事参照下さい。
[Docker入門して機械学習環境構築](https://karaage.hatenadiary.jp/entry/2019/05/17/073000)


# まとめ
　Macでラズパイの仮想環境を構築する最低限の手順を記載しました。全然使い込んでないので、どれだけ実用的なのかは不明です。正直、ラズパイ4買った方が断然良いような気はします。

　GUIを使う方法は分かりませんでした。誰か教えて下さい…

追記：
　GUIを使う方法、実現している方がいました。以下記事参照ください（まだ私は確認できていません）

https://qiita.com/yaju/items/9bb341c1b222590a92ba

　また、目的とOSは違いますが、以下記事でMac+Docker+VS CodeのRemote Container(devcontainer)でGUIが動く仮想環境を構築しています。こちらもよろしければ参考にしてみてください。

https://zenn.dev/karaage0703/articles/1bdb8930182c6c


　ラズパイ4実機のセットアップ方法に関しては、以下記事等参照下さい。初心者は、まずは実機を試すことをおすすめします。

https://karaage.hatenadiary.jp/entry/rpi4-dl-setup

[Raspberry Pi 4のディープラーニングで画像認識する環境をゼロから1時間で構築する方法](https://karaage.hatenadiary.jp/entry/rpi4-dl-setup)

# 参考サイト
　大いに助けられた情報です。

- https://qiita.com/PINTO/items/1cd90f8acca7b5b03685
- https://qiita.com/hishi/items/61652e2d9755e17630de
- http://innossh.hatenablog.com/entry/2018/06/22/170734
- https://github.com/dhruvvyas90/qemu-rpi-kernel
PythonでSMTP通信してみた

PythonでSMTP通信を実装する機会があったので実装方法を書いておきます。

いろいろと実装方法を調べていると標準ライブラリのレガシーAPIで実装している情報はたくさん出てきたのですが、現在推奨されているAPIでの方法があまり見つかりませんでした。WebフレームワークのDjangoもレガシーAPIを使っていたりして、主要ライブラリやフレームワークのコードも参考にできませんでした。
なので、Pythonの`smtplib`と`email`の標準ライブラリを中心にコードリーディングしながら実装しました。

# 環境
- OS: MacOS Catalina
- Python: 3.7
- Library: smtplib, email (Python standard libraries)

# 実装内容

1. SMTPサーバーとのコネクション確立
2. SMTP Auth
3. メール内容送信
4. 複数宛先への送信時に失敗した宛先をログに出力

# ソースコード

```python
import smtplib
from email.message import EmailMessage

class MailClient:
    def __init__(self, mail_data: Dict[str, Any] = {}) -> None:
        """
        Args:
            mail_data(Dict[]): Defaults to {}.
        """

        self._mail_data = mail_data

    def request(self) -> bool:
        """
        Returns:
            bool
        """

        with smtplib.SMTP(host=SMTP_HOST, port=int(SMTP_PORT)) as smtp:
            smtp.login(SMTP_USER, SMTP_PASSWORD)
            errors = smtp.send_message(self.message())

            if isinstance(errors, dict) and len(errors) > 0:
                logger.warn(
                    f'''Failed to send to all recipients.
                    Details: {errors}'''
                )

        return True

    def message(self) -> EmailMessage:
        msg = EmailMessage()
        msg['From'] = self._mail_data['from_address']
        msg['To'] = self._mail_data['to_address']
        msg['Subject'] = self._mail_data['subject']
        msg.set_default_type('text/plain')
        msg.set_content(self._mail_data['message'])

        bcc = self._mail_data.get('bcc')
        if bcc:
            msg['Bcc'] = convert_to_str(bcc)

        cc = self._mail_data.get('cc')
        if cc:
            msg['Cc'] = convert_to_str(cc)

        return msg
```

## 解説

### requestメソッド

`smtplib.SMTP`でSMTPサーバーと接続を開始します。TLSを使用している場合は`smtplib.SMTP_SSL`を使うか、一旦平文でコネクションを張っておいてから`starttls()`でTLSで接続します。
コネクションが正常に確立できたら、 `login()`で認証を行います。その後`send_message`でデータを送信します。実際にデータ送信を担うのは[sendmail](https://docs.python.org/ja/3.7/library/smtplib.html#smtplib.SMTP.sendmail)です。この関数の中でSMTPコマンドを実行してメールを送信します。
エラーハンドリングですが、今回は単純に例外が発生したらraiseするだけでいいのでtry-exceptは書いていません。しかし、複数の宛先に送信した時に一つでも失敗したらログとして残すようにしています。その情報が`send_message`の返り値であり`errors`変数に格納されます。errorsの型はDictでキーが宛先アドレス、値はSTMP応答コードと対応するメッセージを含むタプルです。どのような場合にsend_message / sendmailが値を返すのか詳細を知りたい方は[こちら](https://github.com/python/cpython/blob/3.8/Lib/smtplib.py#L872-L894)にソースコードがありますので一読してみてください。


## messageメソッド

今回はemailモジュールのEmailMessageを使ってメールデータを作成していますが、`send_message`や`sendmail`に直接文字列を代入することはできます。しかし、プロトコルの仕様を理解していないと難しいのでお勧めはしません。できる限りEmailMessageを使用しましょう。

# おわりに
この記事はもっと早くに公開する予定でしたが、途中まで書いて忘れていたのでブログの書き納め?(笑)みたいになってしまいました。
それではよいお年を:snowman2:

# 参考資料
- [smtplib --- SMTP プロトコルクライアント](https://docs.python.org/ja/3.7/library/smtplib.html)
- [Reply Codes by Function Groups](https://tools.ietf.org/html/rfc5321#section-4.2.2)
部分的なOne-Hotエンコーディング

下記のようなDFがあり、CountryカラムのみOne-Hotエンコーディングしたい。

```
Country     |    Age       
--------------------------
Germany     |    23
Spain       |    25
Germany     |    24
Italy       |    30 
```


scikitlearn version 0.20まではOne-Hotエンコーディングしたいカラムのインデックスを `categorical_features` に指定するだけでよかった。
つまりこんな感じ。

```python
from sklearn.preprocessing import OneHotEncoder

onehotencoder = OneHotEncoder(categorical_features=[0])
X = onehotencoder.fit_transform(X)
```
scikitlearn version 0.23からはこのような列ごとに処理が異なるパターンには`ColumnTransformer`を使う。
`remainder="passthrough"`を指定して対象外のカラムを残すのをお忘れなく。

```python
from sklearn.compose import ColumnTransformer 
from sklearn.preprocessing import OneHotEncoder

column_trans = ColumnTransformer(transformers=[('categorical', OneHotEncoder(), [0])],
                                 remainder="passthrough")
X = column_trans.fit_transform(X)
```

## 参考
- [OneHotEncoder categorical_features depreciated, how to transform specific column](https://stackoverflow.com/questions/54345667/onehotencoder-categorical-features-depreciated-how-to-transform-specific-column)
- [sklearn.preprocessing.OneHotEncoder](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html)
- [sklearn.compose.ColumnTransformer](https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html)
【Unity学习笔记】在相机镜头下的前后左右移动实现

## 问题描述
如果使用控制器输入的方向直接移动物体，只能使物体在世界坐标系下或者自身坐标系下前后左右移动，就好像很早以前的游戏里的坦克式移动。如果想操作物体相对于相机镜头做前后左右移动，就需要将输入的向量进行坐标系变换。

## 实现方法
手动坐标系转换可能有一些复杂，好在Unity提供了非常方便使用的坐标系转换用的函数。使用Transform类中的TransformXXX系列的函数即可快速实现坐标系转换。

```csharp：Player.cs
using UnityEngine;

public class Player : MonoBehaviour
{
    [SerializeField]
    private float speed;                // 移动速度

    private Transform cameraTransform;
    private Transform CameraTransform   // 获取主相机的Transform并缓存
    {
        get
        {
            if(cameraTransform == null)
            {
                cameraTransform = Camera.main.transform;
            }
            return cameraTransform;
        }
    }

    private Transform _transform;
    private Transform Transform         // 获取自身的Transform并缓存
    {
        get
        {
            if(_transform == null)
            {
                _transform = transform;
            }
            return _transform;
        }
    }

    private void Update()
    {
        // 取得水平和竖直方向的输入值
        var x = Input.GetAxis("Horizontal");
        var y = Input.GetAxis("Vertical");

        // 没有输入时不执行后续处理
        if (Mathf.Abs(x) < float.Epsilon && Mathf.Abs(y) < float.Epsilon)
        {
            return;
        }

        // 由方向输入来创建一个用于控制玩家位移向量
        var inputVector = new Vector3(x, 0, y);
        // 如果长度大于1则归一化
        if(inputVector.sqrMagnitude > 1f)
        {
            inputVector.Normalize();
        }       

        // 将inputVector从相机坐标系转换到世界坐标系
        var direction = CameraTransform.TransformDirection(inputVector);
        // 去除y方向上的分量并归一化，然后再乘上原本向量的长度，就可以得到一个长度等于输入向量，且平行于世界坐标系XZ平面的向量了
        direction.y = 0;
        direction = direction.normalized * inputVector.magnitude;

        // 使用变换后的向量进行位移，即可实现在相机镜头下的前后左右位移了
        Transform.Translate(speed * Time.deltaTime * direction, Space.World);
        Transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
    }
}
```