OmniAICreator/Qiita-1.07M huggingface dataset:
学習時と推論時のバッチノーマライゼーション

## 概要

### バッチノーマライゼーションを使って学習した後、推論時にはどう入力を正規化すれば良いのか？?

について疑問に思ったので調べてみました。

## 結論

#### BNを用いた学習時に小さくないバッチサイズを使うことができていた場合
学習時のバッチ毎の正規化に使った値の移動平均を記憶させておき、その値を使って推論時の入力も正規化しましょう。

#### BNを用いた学習時に小さいバッチサイズしか使えなかった場合
移動平均ではなく、正規化に使われた値の統計的な平均を計算し、その値を推論時に使いましょう。


## 内容

![bn1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519792/948a3e4b-59b8-82ca-9cd8-29144bd7f4f9.png)
バッチノーマライゼーションは、機械学習において学習時、バッチ毎に正規化を行い、リスケールをするパラメータを学習させることでモデルをより早く学習させたりする汎用的な手法です。

[【GIF】初心者のためのCNNからバッチノーマライゼーションとその仲間たちまでの解説](https://qiita.com/omiita/items/01855ff13cc6d3720ea4)
がとてもわかりやすくバッチノーマライゼーションについて解説しているのでぜひご覧ください。

今回は、学習をより効率化するバッチノーマライゼーションですが、推論時にはどうバッチノーマライゼーションを使えば良いのか疑問に思ったので少し調べて見ました。

## 学習時のBNと推論時のBN
学習はいつもミニバッチサイズの元で行われます。しかしながらテストデータや実際のデータを用いる学習時において、入力はバッチの形で入ってこないことも多いです（もちろんバッチサイズの大きさ並みの入力を一気に推論することもありますが）。そのような時にバッチノーマライゼーションを使って学習したモデルに対して、推論時の入力にはどうノーマライゼーションすれば良いのでしょうか。

[How does batch normalization behave differently at training time and test time?](https://www.quora.com/Does-Residual-Learning-work-without-batch-normalization)
ここにわかりやすく解説してあったのでそれを紹介します。


### BNを学習時に使わない時
もしネットワークをバッチノーマライゼーションを使わないで学習させた場合を考えます。学習にバッチを使わなかったのであればパラメータの更新に１つのデータのみを使いますし、ミニバッチを使ったのであれば、複数のランダムサンプルされたデータを用いて更新値の平均値を取ることで、パラメータを更新する際の勾配をより効率的に求めることができます。

このようにバッチノーマライゼーションを使わずに学習されたモデルに対しては、推論時にはもちろんバッチ化されたテストデータを用いる必要はありません。



### BNを学習に用いた場合

一方で、もしバッチノーマライゼーションを学習に用いた場合、学習時にはバッチノーマライゼーションを用いなかった時のパラメータに加え、バッチノーマライゼーションを使うたびに２つのパラメータを追加で含めることになります。

これは、バッチ毎に正規化されたデータに対し、スケーリングを行い、新たな正規分布へとマッピングを適宜行うためでした。このバッチノーマライゼーションを用いない時に比べて生まれた２つの自由度がネットワークの学習効率を高めていました。



したがって、オペレーションは
1. バッチ間のデータを平均ゼロ、分散１に正規化する
2. 最適な形に正規化された分布をシフト、スケールする

という２つのオペレーションに分けることができました。

２のオペレーションに関して、学習を通してこのシフトとスケーリングのパラメータに関しては学習され、それが他の重みなどのパラメータとともに収束していきます。しかしながら１に関して、どう正規化すれば良いかはもちろんバッチがどう選ばれるかによって変化するものであり、学習されるパラメータではありません。

推論時には、出力は入力にのみ依存し、入力がバッチノーマライゼーションの場所を通るたびに、学習データによって定められたシフトとスケーリングを施されます。
したがって、１の正規化に関しても、学習時に決定されるべきで、それが推論時に２とともに施されるべきなのです。

ここで、これらのノーマライゼーションパラメタは、データ分布に関してに対して正規化をする、つまりデータ分布に関して線形変換を施すものだったことを思い出すと、スケーリング、シフトのパラメータと学習後に組み合わせることができます。


推論時に使う正規化のためのパラメータを取得するため、学習時のミニバッチ学習時の正規化をするときのパラメータが保存されている必要があります。それらを学習時に保存しておいて、のちに平均を取ることでそれを推論時の入力に施すことができます。
また、学習時に正規化につかったパラメータの移動平均を計算しておくことが推奨されており、単に平均を取るよりもアキュラシーが高まるようです。





## 参考文献
- [How does batch normalization behave differently at training time and test time?](https://www.quora.com/Does-Residual-Learning-work-without-batch-normalization)
- [Understanding Batch Normalization](https://medium.com/@krishna_84429/understanding-batch-normalization-1eaca8f2f63e)
- [【GIF】初心者のためのCNNからバッチノーマライゼーションとその仲間たちまでの解説](https://qiita.com/omiita/items/01855ff13cc6d3720ea4)
【Rails】I18n.tはtに省略できるんじゃない、省略したほうが良い

## I18n.tの省略記法
まだなんにもわかっていないRails触り始めて1週間くらいの頃、
**「RailsはViewファイル内では`I18n.t('hoge')`って書かなくても`t('hoge')`って書けるよ、
先輩たちもそう書いてるから合わせてね」**って言われてふむふむなるほど、
よくわからんがviewでは省略できるのかってなった経験はありませんか？

実はI18n.tをtにすると**文字が4文字節約できるってレベルではない変化**が起きていた

## つまりどういう事か
詳しい実相は[この辺](https://github.com/rails/rails/blob/a33dbb17b0818e8a2c75581275374bc0853958ca/actionview/lib/action_view/helpers/translation_helper.rb#L60)
`TranslationHelper`にtが実装されていたんですね

```rails/actionview/lib/action_view/helpers/translation_helper.rb
def translate(key, options = {})
  options = options.dup
  if options.has_key?(:default)
    remaining_defaults = Array.wrap(options.delete(:default)).compact
    options[:default] = remaining_defaults unless remaining_defaults.first.kind_of?(Symbol)
  end

  # If the user has explicitly decided to NOT raise errors, pass that option to I18n.
  # Otherwise, tell I18n to raise an exception, which we rescue further in this method.
  # Note: `raise_error` refers to us re-raising the error in this method. I18n is forced to raise by default.
  if options[:raise] == false
    raise_error = false
    i18n_raise = false
  else
    raise_error = options[:raise] || ActionView::Base.raise_on_missing_translations
    i18n_raise = true
  end

  if html_safe_translation_key?(key)
    html_safe_options = options.dup
    options.except(*I18n::RESERVED_KEYS).each do |name, value|
      unless name == :count && value.is_a?(Numeric)
        html_safe_options[name] = ERB::Util.html_escape(value.to_s)
      end
    end
    translation = I18n.translate(scope_key_by_partial(key), **html_safe_options.merge(raise: i18n_raise))
    if translation.respond_to?(:map)
      translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
    else
      translation.respond_to?(:html_safe) ? translation.html_safe : translation
    end
  else
    I18n.translate(scope_key_by_partial(key), **options.merge(raise: i18n_raise))
  end
rescue I18n::MissingTranslationData => e
  if remaining_defaults.present?
    translate remaining_defaults.shift, options.merge(default: remaining_defaults)
  else
    raise e if raise_error

    keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
    title = +"translation missing: #{keys.join('.')}"

    interpolations = options.except(:default, :scope)
    if interpolations.any?
      title << ", " << interpolations.map { |k, v| "#{k}: #{ERB::Util.html_escape(v)}" }.join(", ")
    end

    return title unless ActionView::Base.debug_missing_translation

    content_tag("span", keys.last.to_s.titleize, class: "translation_missing", title: title)
  end
end
alias :t :translate

```

**ずいぶん色々やってますね**

ざっくりいうとI18n.tに引数を渡す前に該当するロケールが存在しなかった場合の保険とか、
挙動にまつわるオプションの分岐とかががっつり書かれているわけですね

というわけで今後I18n.tってviewに書くプルリクエストが来たら修正してもらいましょう
Ruby 変数に格納された文字列の開始文字数と終了文字数を指定して表示する

# 目的

- 変数に格納された文字列の開始文字数と終了文字数を指定して表示する方法をまとめる

# 書き方の例

- 下記に変数Aに格納された文字列を開始文字数と終了文字数を指定して表示する方法を記載する。

    ```ruby
    puts 変数A.slice(開始文字数..終了文字数)
    ```

# より具体的な書き方の例

- 変数`today`に格納された文字列の0文字目から10文字目までを表示する方法を記載する。

    ```ruby
    today = "2020-02-04 09:01:35"
    puts today.slice(0..10)
    2020-02-04
    ```
業務改善をしよう！目次編

# はじめに
2020年を迎え早一ヶ月が経ちました。
私も一年と少し居た現場を離れ、2020年から新たな現場に向かうこととなりました。
そのためこの一月、新たな現場に慣れるために様々な業務を見て聞いてきました。

**なんだこの面倒くさい業務は!?**

そう感じる業務が多くありました。
前の現場が業務効率化に積極的だったのと、そこで働く人達の意欲も凄かったため、どうしても比較して見てしまっているのかもしれませんが…

**ただ…これはチャンスなのでは？**
前の現場にいたこともあって、おかげで ShellScript の開発を独自で行う機会があったり、API の開発をしていたため通信周りの知識も増えました。
何より、そうした人たちに囲まれていたおかげで業務の効率化に対し積極的な意識がついていました。
まだまだ経験の多くない私ですが、これは貢献あるいはアピールするチャンスなのではと。

というわけで、今の現場で感じた無駄な業務の改善案や、実際になんとかした Script 等を書き記していこうと思います。
この記事は主に改善案等を書き記す目次的な役割を持たせる予定です！
それぞれの記事を作成次第こちらにリンクを張っていこうと思います。

# 目次
- **3媒体全てに同じような内容を登録するのが面倒くさい！**
- **列挙型の一覧をExcel形式で出力するためにcsvファイルをアップロードする！？**
- **リリース時にsqlファイルを逐一指定して実行するのが面倒くさい！**


## 3媒体全てに同じような内容を登録するのが面倒くさい！
ここでいう3媒体というのは下記のタスク管理ツールのことです。
- **BackLog（お客さんとのやり取り用）**
- **Trello（チーム内のタスク管理）**
- **Kintone（会社内のプロジェクト管理）**

これらに同じような内容を登録する必要があるのです。
コピーすれば良いとはいえ、案件ごとに登録をするので面倒！なんとかしたい！
というわけで、それぞれの API を叩いて Script にやってもらおうと考えております。
ただし、チーム内の人にも手軽に使ってもらうことを前提とするため、Windows という関係上 PowerShell で書こうと思っています。


## 列挙型の一覧をExcel形式で出力するためにcsvファイルをアップロードする！？
動きだけ先に紹介します。
現在現場ではバッチ処理を~~週次で動かし、業務データをエクセル形式で出力しています。~~
(2020/2/6 追記)
**→日次だし、出力もcsvでした…というかただファイル移動しているだけ…なにそれ…？**

出力対象は以下のとおりです。
- 各種テーブル
- **列挙型のコード一覧**
テーブルはまだしも列挙型…？
何はともあれなるほど、出力する必要があるのならあるでいいでしょう。

*私「もし列挙型に追加対応があった場合、何かリリース時に必要な作業とかあったりしますか？」*
*チームメンバー「ああ、その場合は出力用にcsvファイルを用意してあるから、リリース時にそれを修正してアップロードしてね」*

**…！？**

え、列挙型のコード一覧をExcel形式で出力するためにcsvファイルを用意するの？？？
というかそもそも列挙型って業務内で変わることないよね？**なんで週次でExcelファイル出力してるの？？？**
**そのアップロードするcsvファイル、Excelファイルにすればよくない？？？**

とまあ、今動いているものを止めるのは私程度の新米では難しいので、この一々csvを修正する手間をなくしたい…
というわけで、jarファイルからクラスファイルをサルベージしてcsvファイルを生成する Script なんかを書けないかなぁと。
最初はそんなことできるの…？と思っていましたが、案外いけそうです！


## リリース時にsqlファイルを逐一指定して実行するのが面倒くさい！
マスタやテーブル自体に変更があった場合、もちろんですが DDL あるいは DML を各環境に適用する必要があります。
それを Linux の画面で、アップロードしておいた sql ファイルをポチポチ名前を指定しては実行を繰り返しています。
そこまでの手間ではないのですが、なんだか面倒くさいなぁと。
それならば Script で指定のディレクトリに格納されている sql ファイルを全て実行するよう組んでしまおうと考えました。
ただリリース作業という手前、慎重に考える必要があると感じています。
これに関してはチームリーダの希望を聞きつつ進めていく予定です。


# 忘れてはいけない大事なこと
今回私にとっては非効率だと感じることが多かったため業務効率化に動こうとしていますが、何より大事なのは**一緒に働くチームの方々の意思です。**
チームリーダからは何か改善点等あればあったらドンドン言ってほしいとは言われているものの、経験も少ない若造にあれは非効率、だからこうしようと、トントン進められたら気分がいいものではないでしょう。
一部のことに関しては流石にどうだろうと思うので改善する方向で動いていますが、メンバーの意思を無視することはチームの崩壊を招きます。
チーム内の調和を保ちつつ、業務効率化を目指して行動したいと思います。
docker入門メモ

# dockerとは

開発環境をコンテナという単位で作るシステム。dockerhubというところにあげて色んな所に開発環境をコピーすることでどの端末でも同じ開発環境で開発できるようになる。

# docker全体像

* dockerfileからimageを作成
* imageは設計書のようなものでこれからcontainerを作成
* このcontainer上でアプリを動かす
* imageはdockerhubで共有できる

# docker用語集

* docker daemon・・・dockerコンテナに指示をする常駐型のシステム
* イメージビルド・・・dockerfileからイメージをつくること

# 仮想化の流れ

![IMG_0053.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193389/68bbe9ff-c674-a26b-7c6f-f35d55ce59a2.jpeg)

* docker デーモン経由でdockerhubからimageをとってくる
* 一度クライアント側にimageを持ってくれば次コンテナを作るときは早くなる（取得しないので）

# 基本的なコマンド

* docker run・・・imageの取得、コンテナの作成などを一気に行う
* docker images・・・イメージの一覧表示（http://docs.docker.jp/engine/reference/commandline/images.html）
* docker pull イメージ名・・・イメージをdockerhubから取得する

より詳細なコマンド一覧
https://qiita.com/wasanx25/items/d47caf37b79e855af95f#up

## 普段の使い方

* docker-compose upでコンテナを起動
* コマンドはdocker-compose runを使い、例えばwebサーバーでrailsのコマンドを打つなら`docker-compose run web rails〜`みたいな感じ

# コンテナ管理

## Kubernetesとdockerswarmの違い

「コンテナ管理ツールのdocekr swarm/Kubernetesについて調べてみた」
https://go-mount.hatenablog.com/entry/2018/09/27/220404

この記事がわかりやすかった。

>・webサービスを提供するにしても、フロントエンドのWebサーバから、webサーバからの処理を返すアプリケーション、データベースなど、複数のアプリケーションが必要。それぞれアプリごとにコンテナを作って、複数コンテナを連携させたい。
>・安定してサービス提供を行うため、データベースはMaster/Slaveの冗長構成で、別々のサーバで、別々にコンテナを動かして連携させたい。
>・新しいコンテナを立ち上げるときは、リソースが比較的空いてるサーバに立ち上げてほしい
>・コンテナがちゃんと起動しているか確認して、停止していたら再起動してほしい

上記の目的で使うもの。Kubernetesのほうが下記で優れているらしい

>docker swarmの時と同じように、管理機能を持つmaster nodeと実際にコンテナを実行させるnodeに分かれています。docker swarmとさらに違う点としては、複数コンテナを集めてPodという単位でkubernetes上ではコンテナを管理します。
Redash環境をGCPに構築する(2020)

最新の情報で日本語化されてるものがなかったので、手順をまとめておきます。
手順は難しくないので、初見でも10分くらいで環境構築できるかなと思います。良い時代ですね。

# 環境
* Google Compute Engine
* Redash 8.0.0
* docker
* Let's Encrypt

# 目標
* Redash環境をGoogle Compute Engine上に作る
* Redashにログインできる
* https化する

# 前提
* GCPプロジェクトが作成できていること
* ドメインが取得できていること

# 参考
この記事の内容は、だいたいここに書いてあることの日本語訳です。
[Setting up a Redash Instance](https://redash.io/help/open-source/setup#gce)

こちらの記事内容を最新にしたものが、この記事の内容となります。（フォーマットを参考にさせていただきました）
https://qiita.com/t-oi/items/6fd670b89ed71a7928d4

# 作業手順
## インスタンスの作成
[GCP上でCloud Shellを開き](https://console.cloud.google.com/compute/instances?cloudshell=true)、以下を実行

```sh
# プロジェクト名を設定
$ gcloud config set project PROJECT_ID

# redashイメージを取得
$ gcloud compute images create "redash-8-0-0" --source-uri gs://redash-images/redash.8.0.0-b32245-1.tar.gz

# インスタンスを作成
$ gcloud compute instances create redash --image redash-8-0-0 --zone asia-northeast1-a
```

## ドメインの紐付け
VPCネットワーク > 外部IPアドレス > タイプ より、「エフェメラル」を「静的」に変更してIPアドレスを永続化。

永続化したIPアドレスを、自身のDNSのAレコードに追加しドメインを紐付け。（ここは省略します）


## Redash側の設定
ここまでで、設定したドメインにアクセスすればセットアップ画面が開くようになっている。
セットアップ画面で必要事項を記入する。

## HTTPS化
[公式の説明](https://redash.io/help/open-source/admin-guide/https-ssl-setup)の上部注意書きの通り、このページの説明は古くなっているので、以下のgistを参考に作業する。
https://gist.github.com/arikfr/64c9ff8d2f2b703d4e44fe9e45a7730e

Compute Engine > VMインスタンス > 接続 > SSH を使い、インスタンスにSSHして作業する。

```sh
$ sudo su
$ cd /opt/redash
$ mkdir nginx && mkdir nginx/certs && mkdir nginx/certs-data

# ファイルの内容はgist参照
$ vi nginx/nginx.conf

# nginxのサービスに、portsとvolumesを追加
$ vi docker-compose.yml

# 再起動
$ docker-compose up -d

# 証明書の発行（ドメイン名を適宜変えてください）
$ docker run -it --rm \
   -v /opt/redash/nginx/certs:/etc/letsencrypt \
   -v /opt/redash/nginx/certs-data:/data/letsencrypt \
   deliverous/certbot \
   certonly \
   --webroot --webroot-path=/data/letsencrypt \
   -d example.redashapp.com
# メールアドレスを聞かれたりするので入力する

# SSL用のnginx.confに書き換え
$ vi nginx/nginx.conf

# 再起動
$ docker-compose restart nginx
```

ここまでで、httpsでアクセスできるようになる。

※GCPインスタンスでhttp接続を許可していないと証明書の更新がうまく行かないので注意。


### 証明書の更新
以下コマンドで更新できる。
（gistにコメントが付いている通り、gistのコマンドは間違っているので注意）

```sh
$ sudo docker run -t --rm \
  -v /opt/redash/nginx/certs:/etc/letsencrypt \
  -v /opt/redash/nginx/certs-data:/data/letsencrypt \
  deliverous/certbot \
  renew \
  --webroot --webroot-path=/data/letsencrypt
$ sudo docker-compose kill -s HUP nginx
```

cronで更新させたい場合は、rootで登録すべきなことに注意する。

```sh
sudo crontab -u root -e
# 毎月1日に実行したければ以下（renew-certificate.shの中身は上）
# 0 0 1 * * sudo sh /opt/redash/renew-certificate.sh
```


## その他
環境変数を変更したければ、 `vi /opt/redash/env` する。
例えば、redashのdatetimeの表示を変更するには、以下の環境変数で変更できる。

```
REDASH_DATE_FORMAT=YYYY/MM/DD
```
Laravel6で Bootstrapを使う方法

こんにちは、ITエンジニアの田中です！

この記事を読むと、Laravel6でbootstrapを使うことができるようになります!

Laravel6から標準でBootstrapがインストールされなくなりました。
そのため、Laravel6でBootstrapを使う場合は、インストールが必要です。

## 前提条件

下記の記事を参照して開発環境を自分のローカルマシン上にdockerコンテナとして持っていることを前提として記事を書きます。
https://qiita.com/ucan-lab/items/17c806973e69792ada99

構成
- PHP7.3
- Laravel6.0
- MySQL8.0
- nginx1.17

## Laravel/uiライブラリのインストール

laravel/ui というライブラリをインストールします

```
[mac] $ docker-compose exec app composer require laravel/ui
```

## artisan uiを実行

下記のコマンドを実行することによりBootstrapがインストールされます。

```
[mac] $ docker-compose exec app ash
[app] $ php artisan ui bootstrap
```

## app.jsとapp.cssの再作成

下記のコマンドでapp.jsとapp.cssを作り直します。

```
[mac] $ docker-compose exec node ash
[node] $ npm install
[node] $ npm run dev
```

## 検証

Viewのxxx.blade.phpにて下記のCSSを参照するようにします。

headタグの間に下記のタグを追加します。

```
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
```

Bodyの下で、app.jsの参照も忘れないようにしてください。

```
<script src="{{ asset('js/app.js') }}"></script>
```

本文に下記のようなタグを入れてください。

```
<div class="alert alert-primary" role="alert">
  A simple primary alert—check it out!
</div>
```

下記のようにBootstrapが適用されていれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282387/de6d2388-7579-87f3-2155-529a86b8bac9.png)

以上です。

みなさんのお役に立てたら、是非この記事にいいねをしてください。
今後の記事作成のモチベーションに繋がります！
macOSにMongoDBをインストールする方法(備忘録)

#このページについて
MongoDBをmacOSに入れようとして``brew install mongodb``としてインストールできませんでした。
公式で確認した手順を忘れないように記録するために作成しています。

#公式サイト

-[公式インストール手順](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/)

#手順
- Homebrewの手順は省略
- tapをする

```
$ brew tap mongodb/brew
```

- インストール

```
$ brew install mongodb-community@N.N(Nは公開されているバージョン)
```

#まとめ
MongoDBはNoSQL(日本語では非SQL)と言われているSQLではないデータベースの一つです。
使ったことがないので、アプリケーションに使う目的で入れてみました。

MySQLと比較してみて、適材適所で選んでいきたいところ
Google Chromeの検索予測をオンにする方法

Googleの検索バーに予測ワードが表示されない時の対応
![スクリーンショット .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569318/a86837a8-ff08-0503-0ffe-fbc52e343a76.png)

対応手順
①chromeの設定画面を開き、左側の『詳細設定』から『プライバシーとセキュリティ』をクリック
![スクリーンショット (4)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569318/9a131ee0-6001-8b6b-4f37-8a58d89d3d82.jpeg)


②『同期とGoogleサービス』をクリック
![スクリーンショット (5)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569318/a4860818-9b72-5456-f21c-9b594c320d3c.jpeg)

③『検索語句やURLをコンプリートする』をオンに変更
![スクリーンショット (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569318/8c6c53bb-80e5-e6b8-0697-bac230634395.png)

これで予測ワードが表示されるようになります。
![スクリーンショット (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569318/6b3fa6b1-ec03-59a3-ece0-f869ea4aaf1f.png)

以上、ご覧いただきありがとうございました。
Android端末1つでDiscordBotを作成し動作させる

「DiscordBotを作ってみたい、だけどPCを持っていない」という人向けにAndroid端末1つでDiscordBotを作って実際に動作させるまでの方法を紹介します。
なるべく初心者向けに説明していますが、Pythonやdiscord.py, Linuxコマンド, Vimコマンドなどの詳しい説明は省略しています。

##動作環境
- Android 9
- Termux 0.90
- Python 3.8.1
- discord.py 1.3.1

#1. Termuxのインストール
TermuxはGoogle Playストアからインストールできます。
https://play.google.com/store/apps/details?id=com.termux

Termuxとはroot化不要でLinux環境を用意することができるエミュレータです。
ここではPythonとVimを使ってDiscordBotを書く方法を説明します。

#2. Termuxでの準備
まずターミナルで以下のコマンドを実行し、aptの更新とPython, Vimのインストールを行います。($はコマンドラインであることを表すもので入力は不要です)

```
$ apt update
$ apt upgrade
$ apt install python
$ apt install vim
```

次にPythonで簡単にDiscordBotを動かすことができるdiscord.pyというライブラリをインストールします。

```
$ pip install discord.py
```
※もしインストールが上手くいかない場合は`pip install yarl`で先にyarlをインストールしておくことで解決する場合があります。

次に実行するPythonファイルを置くディレクトリを作り、移動します。
※{}で囲まれた部分は説明してる内容で置き換えてください

```
$ mkdir {好きなディレクトリ名}
$ cd {上で作ったディレクトリ名}
```

#3. Pythonファイルの作成

```
$ vim {好きなファイル名}.py
```
上のコマンドを実行すると新規ファイルがVimが開かれるので、キーボードで`i`と入力し、INSERTモードにします。
ここで下のコードをコピペしてください。(後述のサイトにあるものを一部改変し拝借させていただきました)

```python

import discord

client = discord.Client()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_message(message):
    # 送り主がBotだった場合反応したくないので
    if message.author.bot:
        return

    # 「おはよう」で始まるか調べる
    if message.content.startswith("おはよう"):
        # メッセージを書きます
        m = "おはようございます" + message.author.name + "さん！"
        # メッセージが送られてきたチャンネルへメッセージを送ります
        await message.channel.send(m)

client.run("token")
```

#4. Botアカウントを作成する
Chromeなどのブラウザで「https://qiita.com/PinappleHunter/items/af4ccdbb04727437477f 」のサイトのサイトを開き、「Bot用のトークンを手に入れる」の項に従ってBotアカウントを作成して使用したいサーバーにBotを追加しておきます。
ここで「後で使います」と言われているトークンをコピーしておいてください。
※このトークンはBotを動かすためのパスワードのようなものなので扱いには十分気を付けてください。

#5. Pythonファイルを実行する
Termuxを開き、`cliant.run("token")`のtokenの部分に、先程コピーしてきたトークンをペーストし、`ESC`→`:wq`でファイルを保存しVimを閉じます。

下のコマンドでPythonファイルを実行します。

```
$ python {先程作成したファイル名}.py
```

これで`Logged in as...`のように表示されればBotが動作していることになります。

Botを追加したサーバーで`おはよう`などを送信して返答が来るか試してみてください。
終了したいときはTermuxを終了すればBotも動かなくなります。もしBotを常時動かしたい場合は、Herokuなどを利用することでできます。
ちなみにTermuxではGitも使えるので、AndroidのみでHerokuにデプロイすることも可能です。

##参考サイト
[Pythonで簡単なDiscord Botの作り方](https://qiita.com/PinappleHunter/items/af4ccdbb04727437477f)
[Android端末上でDiscordのbotを動かす(Termux)](https://qiita.com/F_murasaki/items/5ed82a15b1dde1f2bf9d)
[AndroidでTermuxを使いroot化なしでLinux環境を作る！](https://qiita.com/SquidSky/items/fe13b9ab39fbc1e55491)
wolframalphaでInternet Explorer 11のサポートを間もなく終了するそうです

「Internet Explorer 11のサポートを間もなく終了いたします」
「We are retiring support for Internet Explorer 11」
がでるかも
https://www.wolframalpha.com/input/?i=3x%5E3+%2B+2x%5E2+%2B+x+%2B+10+&lang=ja
Unityのテキストで現在の日付を表示させる

# 1.はじめに　

私はVRのコンテンツを作成しているが、ゴーグルを被って長時間作業すると想定した場合、現在の時間がわからないのは不便だろうと思い記事を書くことにしました。

# 2.Unityの画面①

・Canvas作成手順、テキストUIの追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307841/9c2b4d97-0052-dd7c-e4e7-54e2b4904c9e.png)

・追加後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307841/885aebda-074e-9a1c-63e2-37124289824f.png)


# 3.スクリプトを書く
~~~C#
using UnityEngine;
using UnityEngine.UI; //Textを使用する為追加。
using System; //DateTimeを使用する為追加。

public class Timetext : MonoBehaviour
{
    //テキストUIをドラッグ&ドロップ
    [SerializeField] Text DateTimeText;

    //DateTimeを使うため変数を設定
    DateTime TodayNow;

    void Update()
    {
        //時間を取得
        TodayNow = DateTime.Now;

        //テキストUIに年・月・日・秒を表示させる
        DateTimeText.text = TodayNow.Year.ToString() + "年 " + TodayNow.Month.ToString() + "月" + TodayNow.Day.ToString() + "日" + DateTime.Now.ToLongTimeString();
    }
}
~~~

#4.Unityの画面②
ヒエラキーのTextにスクリプトをコンポーネントし、スクリプトのDateTimeTextにText(UI)をドラッグ&ドロップをする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307841/0dbca15c-8486-6ce0-64ec-4cfd26202c8e.png)




#5.結果
再生ボタンを押すと時間が表示。秒数はリアルタイムで更新されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307841/c491994d-44a3-014f-b46f-950d0672975f.png)
TeraTermでマクロを使わずにSSH転送する

　TeraTermでSSH転送の方法を調べると手動で設定するように説明されているかマクロを使うかの2択が大半ですが、マクロとして書かれているコードをよくよく見ると、やっていること自体は1つのコマンドを実行しているだけで、妙に長いコードは文字列をくっつけ合わせるだけの冗長な処理だったりします。

　SSH転送は次のようにコマンドライン引数を指定すればマクロを使わなくても起動できます。

```bat
"C:¥Program Files (x86)¥teraterm¥ttermpro.exe" "203.0.113.1:22" "/ssh2" "/auth=publickey" "/user=akihiko" "/keyfile=C:¥My¥Settings¥Network¥SshKey.ppk" "/ssh-L10022:192.168.1.1:22"
```

　上記のSSH転送を利用して、最終接続先へアクセスするウインドウは次のように起動します。

```bat
"C:¥Program Files (x86)¥teraterm¥ttermpro.exe" "localhost:10022" "/ssh2" "/auth=publickey" "/user=akihiko" "/keyfile=C:¥My¥Settings¥Network¥SshKey.ppk"
```

## ネットワーク構成

　上記の2つのコマンドは次のようなネットワーク構成を想定しています。

![SshTransport.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157704/f5300974-31ba-622c-3812-642a6b8cabce.png)

　1つめのコマンドによってPCは踏み台サーバーへSSHアクセスし、さらに、PCの10022番ポートを開けます。PCの10022番ポートにアクセスすると、最終接続先の22番ポートまで転送される状態になります。

　2つめのコマンドは、1つめのコマンドで空けて置いた10022番ポートにアクセスするだけです。前述のとおり、これによって最終接続先の22番ポートに接続されます。
AppleScriptを使ってMac画面収録動画をGIFに自動変換

# 動機

[Mac で画面を収録する方法](https://support.apple.com/ja-jp/HT208721#mojave)によると、

> macOS Mojave では、キーボードの「shift + command (⌘) + 5」キーを押すと、画面の静止画を取り込んだり動画で撮影したりするために必要なコントロールが全部表示されます。

ただ、この方法で作成する動画が `.mov` 形式で、BlogやPull Requestにはそのままでは使えないので、FFmpegを使って手動でGIFに変換していました。

これが結構手間で画像を貼り付けるモチベーションが下がるので、今回、Apple Scriptを使い自動化しました。誰かの手間が少しでも省けたら嬉しいです。

[Gyazo](https://gyazo.com) などのツールでも画像を作成してくれるサービスを使わないの？
長めの尺のアニメーションを撮りたい時もあった為です。

# セットアップ

- python3
- ffmpeg

をインストールします。

## GitからソースをDL
[こちらのソース](https://github.com/masa7351/autoMovieConverter)をローカルにDLしてください。

私は、iCloud自動同期されたくないので、画面収録される場所を、デスクトップではなく、`~/workspace/capture/` としていますが、デスクトップや別の場所に変更されている方はパスを変更してください。

※　「shift + command (⌘) + 5」で表示されるツールバーのオプションにて保存先変更が可能です。

### Pythonのインストールパスを変更

Apple Scriptを実行した際に、フルパスで指定する必要があったため、以下のように指定しています。各自の環境に合わせて修正してください。

- scripts/convert.sh

```shell
~/.pyenv/shims/python convert.py
```

## カスタム Apple Script作成

`アプリケーション > ユーティリティ > スクリプトエディタ` で開いて任意のスクリプトを作成できます。
書き出しの際は、複数のフォーマットを選べますが、今回は、`スクリプト`を選んでください。拡張子が、`.script`　で出力されればOKです。

```
property dialog_timeout : 30 -- set the amount of time before dialogs auto-answer.

on adding folder items to this_folder after receiving added_items
    try
        do shell script "~/workspace/capture/scripts/convert.sh"
    end try
end adding folder items to
```

名前は何でも大丈夫です。

作成したファイルを、`/Library/Scripts/Folder Action Scripts` にコピーしてください。

## フォルダアクションの設定

画面収録されるフォルダを選んで、右クリック→サービス→フォルダアクション設定にてフォルダアクションを設定可能です。

ここで設定できるのは、`/Library/Scripts/Folder Action Scripts` に配置されているものだけのようなので注意してください。


<img width="455" alt="setup_folder_action.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45426/3edccfa7-97d7-2733-2c01-659f860bba60.png">

<img width="592" alt="associate_custom_script.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45426/4200b198-c78e-0d78-e1fa-30a7859e872f.png">

<img width="592" alt="setup_folder_action2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45426/06f15038-6c66-a376-1ac8-8a8cbe1aa97c.png">

# 動作

動画収録して、`~/workspace/capture/`に `*.mov` ファイルが保存されると、Apple Scriptが動き、
`~/workspace/capture/converted` にGIF画像が保存されます。
※オリジナルの `*.mov` ファイルを削除していますので、残したい方は `scripts/convert.py` を変更してください。


# 参考
- [Mac で画面を収録する方法](https://support.apple.com/ja-jp/HT208721#mojave)
古いMySQLにインポートする際にハマること

今回、新しいMySQLのデータベースをエクスポートして。古いMySQLにインポートする必要が生じました。
私は、MySQLに詳しくないので、Adminerを使いエクスポートしたものを、そのままインポートしようとしました。

すると、utf8mb4という文字セットを理解できませんという内容の英語のエラーメッセージが表示されました。

ネットで調べると、utf8mb4をUTF8に置き換えるという方法が複数見つかりました。
いくつか試しましたが、どれも成功しませんでした。

別のエラーが出てしまうのです。
ネットの記事を総合すると、数値のデータ項目がある場合は、文字セットや照合順序の置き換えをしてもダメなようです。
確かに、私の場合は、NULLデータのところでエラーになりました。

そのため、クライアント指定の期日に間に合わない事態になってしました。
悔しい気持ちもあり、もう意味がないけどネットで調べていました。

私自身のWordPressの引越しのために、Adminerの使い方を再勉強していた時に、データベースの照合順序の変更ができることを知りました。
![スクリーンショット 2020-02-05 23.07.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20977/48004948-5132-7f1e-879c-e3b2e5332999.png)

画像の中のプルダウンメニューを選択することで、色々と照合順序を変更できることを知りました。

ということは、エクスポートしたいデータベースの照合手順を予めインポートするMySQLが理解できる照合順序に変えて置くことで、問題なくインポートできるように推測しています。

こちらの記事で、古いバージョンのMySQLをインストールする方法を知りました。
https://engineers.weddingpark.co.jp/?p=1284
テスト環境を作って、実証実験したいと思っています。

#はまった時は、落ち着こう
落ち着いてネット検索すると、問題の解決策や解決するヒントを見つけることが出来ます。

##WordPressを日本語環境で使う場合に、推奨される照合順序
こちらの記事が、とても勉強になりました。
https://note.mokeco.in/209/

WordPressを日本語環境で使う場合の照合順序は、utf8_general_ciもしくはutf8mb4_general_ciだそうです。

データベースの文字セットと照合順序を勉強する良い機会になりました。
Pythonによるプログラミング時に使用するライブラリ【開発分類別】

# 概要

Pythonは「pip」が優秀で、情報もあふれているので最近よく使用しています。
ここでは、一つ一つのライブラリの使い方については説明しません。開発分類別に私が採用しているライブラリを列挙し、ライブラリの導入方法について説明します。

# 開発環境

私は特別にこだわりはないので「Anaconda」を使用しています。本当はLinux PCで「virtualenv」を使用し環境を組みたいと思っているけれど、すでにWindowsで環境を組んでしまっているので、面倒くさがっています。


# 開発分類

- Webアプリ開発
- 組込みソフトウェア（センサ制御）
- デスクトップアプリ
- 画像処理
- 機械学習

## Webアプリ開発
サーバサイドのプログラムを作成できます。私が採用していているのは「**flask**」です。PythonのWebフレームワークとしては「Django」ですが、こちらは機能もりもりのフレームワークです。それに対し、flaskは機能は単体では機能が少ない代わりに、覚えることが少ないです。

flaskはサーバサイドレンダリングできるのですが、そのうちflaskはREST API的にサーバサイドの処理だけを担当させ、クライアント側はJavascriptで作成する方が良かったりするので、flaskのレンダリング機能はそこそこで勉強すれば良いと思います。

flaskはpipからインストールできます。

```
pip install flask
```


## 組込みソフトウェア（センサ制御）

ここで対象とするのはセンサ制御です。とはいえ、現在は使い方が簡単なセンサがたくさん出ているので、覚えることは少ないです。基本的には「**pyserial**」を使用すれば良いです。あとはシリアル通信に必要な設定さえ分かっていれば、結構色々なセンサ情報を取得できるので、遊ぶには上等です。


```
pip install pyserial
```

## デスクトップアプリ

私はデスクトップアプリ開発時は「**tkinter**」を使います。
Pythonでのデスクトップアプリ開発ではQtがよく使われているように思いますが、商用の場合はライセンス料が高くて採用できません。また、**tkinterはPython導入時に標準で使用できるので、開発環境構築に悩むことはありません**。


「**tkinter**」は表のようにフレームを配置するイメージでいれば、そこまでデザインに悩む必要はありません。

## 画像処理

画像処理であれば「**OpenCV**」一択のような気がします。最近は機械学習系のこともできるらしいですが、

```
pip install numpy
pip install opencv-python
```
## 機械学習

機械学習であれば、情報量として「**Tensorflow+keras**」を選択するのが現在は安全なのかと思います。今は「PyTorch」が人気を上げてきているように思いますが、情報だけ取得するくらいでまだ良いと思います。

構造的にはTensorflowの中にkerasが内蔵されている形になっています。TensorflowはAnacondaからインストールした方が良いです。私の別の記事（メモ？）で説明しています（[https://qiita.com/StrayDog/items/5b91096133ea65bfd5ec](https://qiita.com/StrayDog/items/5b91096133ea65bfd5ec)）。

でも、最近少し「pytorch」が気になっている。

```
pip install numpy
```
IFRAMEにコンテンツをフィットさせるには

固定サイズのHTMLを、それよりも小さいサイズのIFRAMEに縮小して表示したかったので、その対処方法です。

# 課題

たとえば1440px X 1000pxの大きさのHTMLを幅800pxのIFRAMEに表示します。

# 解決方法

```index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="default.css">
  </head>
  <body>
    <div id="wrapper">
      <h1>Resize 1440px X 1000px iframe to width 800px</h1>
      <iframe id="iframe" src="iframe-content.html"></iframe>
    </div>
    <a href="iframe-content.html" target="_blank">iframe-content.html</a>
  </body>
  <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
  <script src="default.js"></script>
</html>
```

```default.css
body {
  text-align: center;
}
#wrapper {
  margin: 0 auto 0 auto;
  overflow: hidden;
  width: 800px;
}
#iframe {
  height: 1000px;
  width: 1440px;
}
```

```default.js
(function (window, document, $) {
  let iframe = $('#iframe');
  let wrapper = iframe.parent();
  let width = wrapper.width();
  let ratio = width / iframe.width();
  console.log(`Ratio: ${ratio}`);

  // IFRAME自体は読み込みページの大きさにCSSで適用している。
  // それを#wrapperのサイズにスケールインする。
  // https://stackoverflow.com/questions/166160/how-can-i-scale-the-content-of-an-iframe
  iframe
    .css('-ms-transform',     `scale(${ratio})`)
    .css('-moz-transform',    `scale(${ratio})`)
    .css('-o-transform',      `scale(${ratio})`)
    .css('-webkit-transform', `scale(${ratio})`)
    .css('transform',         `scale(${ratio})`)
    .css('-ms-transform-origin',     '0 0')
    .css('-moz-transform-origin',    '0 0')
    .css('-o-transform-origin',      '0 0')
    .css('-webkit-transform-origin', '0 0')
    .css('transform-origin',         '0 0');

  // #iframeのひとつ上のラッパー#wrapperの高さを同じ倍率で変更する。
  // これをしないとうまくもともとのIFRAMEの高さのままになる。
  wrapper.height(wrapper.height() * ratio);

})(window, window.document, window.jQuery); 
```

## ポイント

- 同じ倍率で高さも変える必要もあるので、JavaScriptで対応します。
- 参考サイトにある通り、CSSで縮小します。
- IFRAMEのサイズは読み込んでいるコンテンツのサイズと同じにしておく。
- IFRAMEをラッパーで囲みます。CSSで指定したサイズの領域が取られているのでラッパーのdivで高さを調整しています。
- 面倒だったのでjQueryを使用。

[demo](http://tom-gs.com/demo/test/fit-iframe/)

# 参考サイト

[How can I scale the content of an iframe?](https://stackoverflow.com/questions/166160/how-can-i-scale-the-content-of-an-iframe)
Laravel6でpublicフォルダをpublic_htmlへ変更する方法

こんにちは、ITエンジニアの田中です！
この記事を読むと、Laravel6で、publicフォルダをpublic_htmlにすることができます。

## 変更する理由
レンタルサーバーによっては、public_htmlが公開フォルダになっており、Laravelで構築したプログラムを配備して公開するためには、publicフォルダではなくpublic_htmlフォルダを公開する必要があるためです。

## 検証環境

下記の記事を参照して開発環境を自分のローカルマシン上にdockerコンテナとして持っていると仮定します！
https://qiita.com/ucan-lab/items/17c806973e69792ada99

- PHP7.3
- Laravel6.0
- MySQL8.0
- nginx1.17  
※ Laravelは最新版使いたかったので、6にしています。

## server.php

下記のファイルの最後の行を編集します。
src/server.php

Before

```
require_once __DIR__.'/public/index.php';
```

After

```
require_once __DIR__.'/public_html/index.php';
```

## AppServiceProvider.php

下記のファイルのregister関数を下記のように変更します。
src/app/Providers/AppServiceProvider.php

```
    public function register()
    {
        //
        $this->app->bind('path.public', function() {
            return base_path().'/public_html';
        });
    }
```

## filesystems.php

下記のファイルのstorage_pathを変更します。

src/config/filesystems.php

```
        'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public_html'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
        ],
```

## webpack.mix.js

src/webpack.mix.js

Before

```
mix.js('resources/assets/js/app.js', 'public/js')
  .sass('resources/assets/sass/app.scss', 'public/css');
```

After

```
mix.setPublicPath('public_html/');
mix.js('resources/js/app.js', 'js')
mix.sass('resources/sass/app.scss', 'css');
```

## CSSとJSの再作成

下記のコマンドを打つことによりapp.cssとapp.jsをpublic_htmlの下に作り直します。

```
[mac] $ docker-compose exec node ash
[node] $ npm install
[node] $ npm run dev 
```

## dockerコンテナーの再起動

```
[mac] docker-compose down
[mac] dpcker-compose up
```

上記で再起動して、下記のURLにアクセスできれば完了です！
http://localhost:10080/

みなさんのお役に立てたら、是非この記事にいいねをしてください。
今後の記事作成のモチベーションに繋がります！

お願いします。
GAS(Google Apps Script)で現在のシートのIDを取得


GASを触っていたのでメモ

## GoogleスプレッドシートのIDとGID

* スプレッドシート: Googleドライブ的に見たときの一つのファイル
* シート: スプレッドシート内のシートタブ

って感じになります。

スプレッドシートのURLはこんな感じです。

```
https://docs.google.com/spreadsheets/d/XXXXXXXXXXXX/edit#gid=YYYYYYYY
```

`XXXXXXXXXXXX`の部分がスプレッドシートのID、`YYYYYYYY`の部分がシートのID(GID)になります。

## GASでアクティブなシートのIDを取得

`getActiveSpreadsheet()` > `getActiveSheet()` > `getSheetId()`という感じで階層を掘って取得します。

```getGid.gs
function pingTest() {
  const ss = SpreadsheetApp.getActiveSpreadsheet(); //スプレッドシート
  const activeSheet = ss.getActiveSheet(); //スプレッドシート内のアクティブなシート
  const gid = activeSheet.getSheetId(); //アクティブなシートのID
}
```

## GIDを取得して他のサーバーに送る

`https://hogehoge.hugahuga.com/?gid=YYYYYYYY`という感じでURLのクエリに詰めて飛ばします。 (POSTじゃなくてGETリクエストだけど)

```post.gs
function pingTest() {
  //GID取得  
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const gid = sheet.getSheetId();
  
  //GETリクエストで送る
  const APPURL = 'https://hogehoge.hugahuga.com/';
  const url = APPURL + '?gid=' + gid;    // 監視対象URL
  UrlFetchApp.fetch(url, { muteHttpExceptions:true });
}
```
はじめてのパターン認識 第１章

## はじめに
[はじめてのパターン認識](https://amzn.to/37iEey0) を読み進めて理解した内容を章ごとにまとめていきます。随時アップデート予定。

## 各章のまとめ
- [はじめてのパターン認識 第１章 - この記事](https://qiita.com/drafts/3067edb7019665d8d3b9/edit)

## 事前知識
- パターン認識という分野がある
   - 画像や音声といったデータの中に潜む何かしらのルールを自動で抽出する方法を研究する学問
   - パターン認識を実現するための１つの手法として機械学習があるが、パターン認識＝機械学習と考えてもよい

## 1.1 パターン認識とは
- パターン
   - 型、類型
- 認識
   - 人間が物事を知る働き、およびその内容
   - よって **パターン認識** とは物事の類型を知るはたらき、およびその内容
- 識別
   - 何かを判断すること
- 特徴抽出
   - 判断するために必要な特徴を抽出すること
   - **特徴ベクトル** とは抽出された特徴を並べてベクトルの形にまとめたもの
      - 例：硬貨の透磁率や重さ、サイズ、穴の有無など
- 識別規則
   - 入力データが所属する正しいクラスを同定するために規則
   - 識別規則を作るためには、入力データとそのクラスを対にしたたくさんの事例を使って、入力データとクラスの対応関係を学習する必要がある
- 汎化能力
   - 未知の入力データに対して正しいクラスを識別する能力

## 1.2 特徴の型
### 定性的特徴
- 非数値データとして抽出された特徴
- 名義尺度も順序尺度も比例尺度である出現頻度に変換して取り扱うことが多い
- ２つのクラスラベルを表すには、0, 1 といったように符号を用いる
   - クラス数が２個以上ある場合は、N個の２値変数を用意し、クラスに対応する変数のみを１として他を０とする符号化を行う **ダミー変数** 化を行う
- 名義尺度
   - 他と区別し分類するための名称のようなもの
   - 変数値間の比較は等しいか異なるかでしか行えない
   - 離散変数、カテゴリ変数ともよばれる
   - 例：男女、血液型、郵便番号、住所、本籍地、所属学部、学籍番号
- 順序尺度
   - 順序や大小には意味があるが間隔には意味がないもの
   - 比較はできるが、加減算などの演算はできないc
   - 例：1位 / 2位 / 3位…、1. 好き / 2. ふつう / 3. 嫌い

### 定量的特徴
- 数値データとして抽出された特徴
- 間隔尺度
   - 目盛が等間隔になっているもので、その間隔に意味があるもの
   - 原点はあっても無ではない
   - 例えば、気温が19℃から1℃上昇すると20℃になるとは言えるが、10℃から20℃に上昇したとき、2倍になったとは言えないもの
   - 例：カレンダーの日付、試験の成績
- 比例尺度
   - 0が原点であり、間隔と比率に意味があるもの
   - 例えば、身長が150cmから30cm伸びると180cmになると言えるし、1.2倍になったとも言えるもの
   - 例：身長、速度、睡眠時間、値段、給料、幅跳びの記録

## 1.3 特徴ベクトル空間と次元の呪い
- 適応制御において未知の複雑な関数を学習するために必要なデータが次元の増加と共に指数関数的に増加すること
- 特徴数が n個であれば特徴ベクトルは n次元線型空間を張る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184983/931874a3-eba8-daf0-7a02-b9a322cfb724.png)
- 16 * 16 = 256 個の画素値からなる手書き文字の画像データを256次元と表現する
   - 各画素が 16レベルの段階を持っていた場合、$16^{256}$ となり、１枚の画像は少なくとも256桁ある区画の中の１つの区画を埋めるだけである

## 参考
- [写像への目覚めー機械学習とは写像の構築であるー](https://thinkit.co.jp/article/16322)
- [尺度水準 - Wikipedia](https://ja.wikipedia.org/wiki/%E5%B0%BA%E5%BA%A6%E6%B0%B4%E6%BA%96)
- [変数の尺度 - 統計WEB](https://bellcurve.jp/statistics/course/1562.html)
- [はじめてのパターン認識 第1章 - SlideShare](https://www.slideshare.net/Prunus1350/1-23146712)
JPQLでLIMIT, OFFSET

#やりたいこと
Personテーブル(カラムはid, name, age)に１００件レコードを用意し、指定した位置から任意の数だけレコードを取得したい。

```
@PostConstruct
	public void init() {
		for(int i = 1; i < 101; i++) {
			Person p = new Person("テストユーザー" + i, 18); //100人とも永遠の１８さ(ry
			repository.saveAndFlush(p);
		}
	}
```

#問題
```
public List<Person> find(){
		List<Person> list = null;
		String str = "from Person limit 20 offset 10";
		Query query = en.createQuery(str);
		list = (List<Person>)query.getResultList();
		return list;
	}
```
こんな感じで適当に11行目から20件取得しようとしたら、

```
There was an unexpected error (type=Internal Server Error, status=500).
org.hibernate.hql.internal.ast.QuerySyntaxException: 
unexpected token: 20 near line 1, column 36 [from com.example.demo.Person limit 20 offset 10]
```
構文エラーだと。。。

#解決策
JPQLだと、

OFFSET => setFirstResult(n)
LIMIT => setMaxResults(n)

って記述らしい。。。
調べが足りなかった。

```
public List<Person> find(){
		List<Person> list = null;
		String str = "from Person";
		Query query = en.createQuery(str).setFirstResult(10).setMaxResults(20);
		list = (List<Person>)query.getResultList();
		return list;
	}
```
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440460/0b13d2d4-3766-6116-345d-8b4add85a5a2.png)



##参考
http://enterprisegeeks.hatenablog.com/entry/2015/02/16/080922
macOS Catalina(10.15.3)で、Homebrewで入れたApacheが起動しない問題への対応

## 現象
macOS Catalinaを10.15.3にアップデートしたら、Homebrewで入れたApacheが起動しなくなった。

```
# ブラウザでlocahostにアクセスしても何も表示されない
$ sudo apachectl start
```

## 調査
Apacheのログを調べてみる。

### Apacheのログファイルの場所
Apacheのログファイルの場所は、`httpd.conf`に書かれている。ApacheはHomebrewで入れたので、パスは`/usr/local/etc/httpd/httpd.conf`になる。

```
$ view /usr/local/etc/httpd/httpd.conf
...
ErrorLog "/usr/local/var/log/httpd/error_log"
...
```
### ログを見る
```
$ tail -1000 /usr/local/var/log/httpd/error_log
...
[Wed Feb 05 22:33:14.400907 2020] [ssl:warn] [pid 20851] AH01873: Init: Session Cache is not configured [hint: SSLSessionCache]
[Wed Feb 05 22:33:14.401430 2020] [cgid:error] [pid 20852] (2)No such file or directory: AH01243: Couldn't bind unix domain socket /usr/local/var/run/httpd/cgisock.20851
[Wed Feb 05 22:33:14.402081 2020] [http2:warn] [pid 20851] AH10034: The mpm module (prefork.c) is not supported by mod_http2. The mpm determines how things are processed in your server. HTTP/2 has more demands in this regard and the currently selected mpm will just not do. This is an advisory warning. Your server will continue to work, but the HTTP/2 protocol will be inactive.
[Wed Feb 05 22:33:14.402221 2020] [lbmethod_heartbeat:notice] [pid 20851] AH02282: No slotmem from mod_heartmonitor
[Wed Feb 05 22:33:14.403549 2020] [core:error] [pid 20851] (2)No such file or directory: AH00099: could not create /usr/local/var/run/httpd/httpd.pid
[Wed Feb 05 22:33:14.403572 2020] [core:error] [pid 20851] AH00100: httpd: could not log pid to file /usr/local/var/run/httpd/httpd.pid
```

### 解決策
ログを見る限り、`/usr/local/var/run/httpd/httpd.pid`が作られないようだ。ディレクトリを確認すると、httpdディレクトリがなかった。httpdディレクトリを手動で作ると動くようになった。

```
# runディレクトリ以下には何もない
$ cd /usr/local/var/run
$ ls -la 
total 0
drwxr-xr-x  2 username  admin   64  2  5 22:33 .
drwxrwxr-x  9 username  admin  288  2  5 22:33 ..

# httpdディレクトリを作成
$ mkdir httpd
$ ls -la
total 0
drwxr-xr-x  3 username  admin   96  2  5 22:35 .
drwxrwxr-x  9 username  admin  288  2  5 22:33 ..
drwxr-xr-x  2 username  admin   64  2  5 22:35 httpd

# ブラウザでlocalhostにアクセスできるようになった！
$ sudo apachectl start

# cgisock.21666 と httpd.pidが作成されている
$ ls -la /usr/local/var/run/httpd
total 8
drwxr-xr-x  4 username  admin  128  2  5 22:35 .
drwxr-xr-x  3 username  admin   96  2  5 22:35 ..
srwx------  1 _www      admin    0  2  5 22:35 cgisock.21666
-rw-r--r--  1 root      admin    6  2  5 22:35 httpd.pid
```
"色違いポケモンシミュレータ"

乱数を使う練習①
アイテムなしで色違い孵化厳選を行った場合のシミュレーション

import java.util.Random;

public class Irochigai
{
	public static void main(String[] args)
	{
		System.out.println("色違いシミュレータ");

		Random r = new Random();
		int randomValue = r.nextInt(4096);

		if (randomValue == 4096) {
			System.out.println("Irochi");
		}
		else {
			System.out.println("normal");
			}
		System.out.println(randomValue);
	}
}

＜今後の予定＞
・「キーを押してシミュレーションを続行する」を実装
・アイテムや厳選方法による条件の変化に対応
【Rails6】DEPRECATION WARNING: update_attributes is deprecated and will be removed from Rails 6.1 (please, use update instead)

# TL;DR
`update_attributes` => `update` に変更しましょう。
※安心してください。動作は変わりません。

# 環境
- Ruby: 2.6.3
- Rails: 5.2.4.1 => 6.0.2.1 へアップデート

# なんかWarning出たな？ :thinking: 

```
DEPRECATION WARNING: update_attributes is deprecated and will be removed from Rails 6.1 (please, use update instead)
```

`update_attributes`, `update_attributes!` を使っているところでこんなWarningが出ました。

`update_attributes` は Rails 6.1 で削除するから、代わりに `update` を使ってねって言われています。

# 変更しちゃって大丈夫？ :thinking: 

Railsのソースを見てみましょう。
https://github.com/rails/rails/blob/v6.0.2.1/activerecord/lib/active_record/persistence.rb#L625

```ruby
alias update_attributes update
deprecate update_attributes: "please, use update instead"
```

`update_attributes` は `update` のaliasとして定義されています。
なので、どちらを使っても動作は変わらないです。

`update_attributes!` も同様ですね。
https://github.com/rails/rails/blob/v6.0.2.1/activerecord/lib/active_record/persistence.rb#L639-L640


ガッと `update`, `update!` へ変更してWarning解消出来ました。
node.js の --max-old-space-size のデフォルト値は 1400MB

node.js の V8 のヒープのメモリ容量を設定するオプション --max-old-space-size のデフォルト値は 1.4GB みたい。

https://github.com/nodejs/node/blob/master/deps/v8/src/heap/heap.cc

```cc
    max_old_generation_size_ = 700ul * (kSystemPointerSize / 4) * MB;
```

64bit OS だと `kSystemPointerSize` は 8 だろうから、1400MB になりそう。
実際の値をみると、`--max-old-space-size=1400` の結果とマッチした。 

```
node --max-old-space-size=1000 -e 'console.log(Math.floor(v8.getHeapStatistics().heap_size_limit/1024/1024))'
# 1049

node --max-old-space-size=1400 -e 'console.log(Math.floor(v8.getHeapStatistics().heap_size_limit/1024/1024))'
# => 1456

node -e 'console.log(Math.floor(v8.getHeapStatistics().heap_size_limit/1024/1024))'
# => 1456

node --max-old-space-size=2000 -e 'console.log(Math.floor(v8.getHeapStatistics().heap_size_limit/1024/1024))'
# => 2066

node --max-old-space-size=3000 -e 'console.log(Math.floor(v8.getHeapStatistics().heap_size_limit/1024/1024))'
# => 3083
```

まとめ：
`--max-old-space-size=1000` にすると、デフォルトより3割くらい減る。
`--max-old-space-size=2000` にすると、デフォルトより4割くらい増える。
`--max-old-space-size=3000` にすると、デフォルトより2倍ちょっと増える。

他の記事で `--max-old-space-size=2000` をよくみるのは、増やしすぎない按配なのか。
クラスの多重継承

```py:1
class Person(object):
    def talk(self):
        print('talk')
    def run(self):
        print('person run')

class Car(object):
    def run(self):
        print('car run')

class PersonCarRobot(Person, Car):
    def fly(self):
        print('fly')

person_car_robot = PersonCarRobot()
person_car_robot.talk()
person_car_robot.run()
person_car_robot.fly()
```

```:1の実行結果
talk
person run
fly
```
PersonCarRobotクラスは、
PersonクラスとCarクラスの両方を継承している。
なので、
両方のクラスのメソッドを持っている。

ここで、
PersonクラスもCarクラスも runというメソッドを持っている。

この場合、
class PersonCarRobot(Person, Car)となっており、
先にPersonクラスを継承して、ないものをCarクラスから継承するイメージ。
なので、Carクラスのrunメソッドは継承されない。

class PersonCarRobot(Car, Person)と入れ替えると
先にCarクラスを継承して、ないものをPersonクラスから継承する。
Personクラスのrunメソッドは継承されない。

```py:2
class Person(object):
    def talk(self):
        print('talk')
    def run(self):
        print('person run')

class Car(object):
    def run(self):
        print('car run')

class PersonCarRobot(Car, Person):
    def fly(self):
        print('fly')

person_car_robot = PersonCarRobot()
person_car_robot.talk()
person_car_robot.run()
person_car_robot.fly()
```

```:2の実行結果
talk
car run
fly
```
terraform で Error putting S3 policy: MalformedPolicy  が出たときに確認したこと

#  症状
[こちらの投稿](https://qiita.com/natsumisawa/items/404217208ab1c96d8719) を参考に
terraformで構築、と拡張していたところ、S3のポリシー関連でエラーが出るようになった

## エラーメッセージ
```
Error: Error putting S3 policy: MalformedPolicy: Action does not apply to any resource(s) in statemen
	status code: 400, request id: hoge, host id: fuga
```

# 原因
`aws_iam_policy_document` の `statement` `resources` に必要なARN指定が不足していた。


```
data "aws_iam_policy_document" "cf_to_s3_policy" {
  statement {
(中略)
    resources = [
      // "${aws_s3_bucket.image-bucket.arn}" の行が不要だと思って削除したが、実は必要だった
      "${aws_s3_bucket.image-bucket.arn}/testdir/*",
    ]
(中略)
```


# 修正後のソースコード 
```
data "aws_iam_policy_document" "cf_to_s3_policy" {
  statement {
    actions = ["s3:GetObject", "s3:ListBucket"]

    resources = [
      "${aws_s3_bucket.image-bucket.arn}",
      "${aws_s3_bucket.image-bucket.arn}/*",
    ]

    principals {
      type        = "AWS"
      identifiers = ["${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}"]
    }
  }
}
```

# 参考
https://qiita.com/natsumisawa/items/404217208ab1c96d8719
https://stackoverflow.com/questions/44228422/s3-bucket-action-doesnt-apply-to-any-resources
Next.jsでCSSを読み込む方法

#Next.jsでCSSを読み込むときは、Headを定義しlinkタグにて読み込む

以下のような感じ
linkタグで読み込めばOK

```
import React from "react";
import Header from "../includes/header";
import Head from "next/head";

const MainLayout = props => {
  return (
    <>
      <Head>
        <title>My Awesome app</title>
        <link
          href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
          rel="stylesheet"
        />
        <link
          href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
          crossorigin="anonymous"
        />
        <link href="#" rel="stylesheet" />
      </Head>
      <Header /> {props.children}
    </>
  );
};

export default MainLayout;
```
Rubyの基礎まとめ[メモ]

コーディングテスト用のメモ。

# 基礎文法

### 繰り返し

- for

```ruby
for i in 1..3 do
  puts i
end
```

※配列でも可 

```ruby
for num in [1, 2, 3] do
  puts num
end
```

- each

```ruby
[1, 2, 3, 4].each do |num|
  p num
end
```

- while

```ruby
num = 0 #初期値
while num <= 12 do #条件
  p num #処理
  num += 3 #値の更新
end
```

- times

```ruby
3.times do |num|
  p num
end
```

### 各桁の和

```ruby 
a = 11
puts a.to_s.split('').map{|x| x.to_i}.inject(:+)
#=> 2
```

[String#chars](https://docs.ruby-lang.org/ja/2.7.0/method/String/i/chars.html)

```ruby
a = 11
puts a.to_s.chars.map{|x| x.to_i}.sum

### 奇数or偶数番目要素の取得
```

[Integer#digits](https://docs.ruby-lang.org/ja/2.7.0/method/Integer/i/digits.html)

```ruby
a = 11
puts a.digits.sum
```

### 条件分岐(書き換えパターン)

- pattern(1)

```ruby
s = "hoge"
if s==hoge
  puts "YES"
else
  puts "NO"
end

#=>YES
```

- pattern(2)

```ruby
s = "hoge"
puts s == hoge ? "YES":"NO"

#=>YES
```

- pattern(3)

```ruby
s = "hoge"
case s
when "hoge" then
  puts "YES"
when "no_hoge" then
  puts "NO"

#=>YES
```

### .gsub(パターンマッチング)

```ruby
string = "ruby ruby ruby"
puts string.gsub(/ruby/, 'python')

#=> python python python
```

### 数値の切り捨て

```ruby
1.4.floor # 1
1.5.floor # 1
-1.4.floor # -2
-1.5.floor # -2
```

### 数値の切り上げ

```ruby
1.4.ceil  # 2
1.5.ceil  # 2
-1.4.ceil # -1
-1.5.ceil # -1
```




# 配列の操作

### 配列の割り算
 
```ruby
a = [2, 4, 6]
b = a.map(|x| x/2)
puts b

#=> [1, 2, 3]
```

### 配列の合計

```ruby
a = [1, 2, 3]
puts a.inject(:+)

#=> 6
```

```ruby
ary = ('a'..'j').to_a
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]

# 偶数番目
ary.select.with_index { |_, i| i.even? }
#=> ["a", "c", "e", "g", "i"]

# 奇数番目
ary.select.with_index { |_, i| i.odd? }
#=> ["b", "d", "f", "h", "j"] 
```

### 並べ替え

```ruby
ary = [2, 4, 1, 5, 3]

# 昇順 
ary.sort
#=> [1, 2, 3, 4, 5]

# 降順
ary.sort.reverse
#=> [5, 4, 3, 2, 1]
```


### 重複要素を統一

```ruby
ary = [1, 2, 2, 3, 3]

ary.uniq
#=> [1, 2, 3]
```

### 配列→文字列

```ruby
ary = [1, 2, 3, 4, 5]

ary.join(' ')
#=> '1 2 3 4 5'
```

### 要素の追加

```ruby
ary = ['a', 'b', 'c']

ary << 'd'
#=> ['a', 'b', 'c', 'd']
```

### 最大値最小値

```ruby
ary = [1, 25, 50, 75, 100]

ary.minmax
#=> [1, 100]
```
GraphQLでファイルアップロード

# GraphQLでファイルアップロード

GraphQL(Appolo Server)でファイルをアップロードする。
サーバ側はnode.jsを使う。
クライアントは[Altair GraphQL Client](https://altair.sirmuel.design/)と使う。

サンプルのソースコードは以下。

- [ymatzki/graphql-server-example: Learn GraphQL.](https://github.com/ymatzki/graphql-server-example)

## 事前準備

[Get started with Apollo Server - Apollo Server - Apollo GraphQL Docs](https://www.apollographql.com/docs/apollo-server/getting-started/)に従う。

```
$ mkdir graphql-server-example ;\
  cd graphql-server-example
$ npm init --yes
$ npm install apollo-server graphql
```

## サーバ

[File uploads - Apollo Server - Apollo GraphQL Docs](https://www.apollographql.com/docs/apollo-server/data/file-uploads/)をほぼそのまま使う。
若干の変更として、アップロードが成功したときにメッセージを表示するように変更。

```file-uploads/index.js
const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type File {
    filename: String!
    mimetype: String!
    encoding: String!
  }

  type Query {
    uploads: [File]
  }

  type Mutation {
    singleUpload(file: Upload!): File!
  }
`;

const resolvers = {
  Query: {
    uploads: (parent, args) => {},
  },
  Mutation: {
    singleUpload: (parent, args) => {
      return args.file.then(file => {
        console.log(`📁 File get ${file.filename}`);
        return file;
      });
    },
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});
```

サーバを起動する。

```
$ node file-uploads/index.js
🚀 Server ready at http://localhost:4000/
```

## クライアント

アップロードするファイルの準備。

```
echo "FOO" > file-uploads/foo.txt
```

[Altair GraphQL Client](https://altair.sirmuel.design/)でファイルをアップロードする。

![altair.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13809/1259479e-d13d-0669-a8ca-c27bee2c4d55.gif)

それぞれ以下を入力し`Send Request`を実行。

- URL  

```
http://localhost:4000
```

- graphQL query

```
mutation($file: Upload!){
  singleUpload(file: $file){
    filename
    mimetype
    encoding
  }
}
```

- Choose file  

```
foo.txt
```

- File name  

```
file
```

成功すればサーバに以下のログが出力される。

```
📁 File get foo.txt
```
【お手軽Python】pandasによるExcelファイル読み込み

#pandasとは
pandasとは、Pythonのライブラリです。
pandasは、主にデータを操作する時に使われます。AIや機械学習にpandasは必須です。
ですが、Excelのファイルを操作する機能も搭載されています。
今回は、pandasを使ったExcelファイルの読み込みの仕方について説明していきます。

#Excelファイル読み込み

###最初のシートのみの読み込み

sample.xlsxの中身が
![bandicam 2020-01-20 16-36-57-804.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240459/c815e4d5-f406-100b-39f2-fa83c8fe0fba.jpeg)
だった場合

```python:main.py
import pandas as pd
df = pd.read_excel('sample.xlsx')
print(df)
```

上記のmain.pyを実行すると
![bandicam 2020-01-20 16-44-46-679.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240459/1bb18864-9605-e344-8ac0-f73dcdad0364.jpeg)
となり、無事Excelファイルを読み込む事に成功しました。

###全シートから読み込み

因みに、main.pyの二行目を

```python
df = pd.read_excel('sample.xlsx', sheet_name =None)
```

とすれば、sample.xlsxの全てのシートからデータを取ってくる事ができます。

###指定したシート読み込み

####番号で指定

sheet_nameをNoneにすると、全シート読み込みとなりました。
sheet_nameに0から始まる番号を指定することで、[指定した数字+1]番目のシートを読み込むことができます。

```python
df = pd.read_excel('sample.xlsx', sheet_name =0) #1枚目のシート読み込み
df = pd.read_excel('sample.xlsx', sheet_name =2) #3枚目のシート読み込み
```

####名前で指定

sheet_nameに文字列を指定することで、その文字列をシート名に持つシートを読み込むことができます。

```python
df = pd.read_excel('sample.xlsx', sheet_name ='sheet1') #名前が'sheet1'のシート読み込み
```

#まとめ

pandasを使ってExcelファイルを読み込む方法を紹介しました。
この機能はpandasの全機能の氷山の一角に過ぎません。

非常に様々な場面で使われるライブラリなので、気になった方は是非詳しいサイトなどを調べてみてください。
Evernote からの移行先を GitHub にしてみる

プライベートな技術メモをどこに取るかについて。

色々試しましたが、これだと思えるメモアプリが見つからず、仕方なく Evernote を使い続けていました。

よく考えてみたら、自分がほしいのは GitHub みたいな使い勝手のメモアプリで、だったらそのまま GitHub を使えばいいのでは、、、というわけで、プライベートリポジトリの issue にメモを取ってみています。

<img alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/3e0225ea-d2ac-4278-a9c7-70cf051a8b77.png">


## 良いところ

 * Markdown
 * 画像をドラッグドロップで貼れる
 * GitHub と同じ見た目のシンタックスハイライト
 * 適切な単位 (コメント) 毎にパーマリンク
 * サンプルコードを入れるリポジトリ
 * 検索
 * タグ
 * クラウド
 * モバイル
 * ファイル添付
 * API
 * カンバン
 * チーム内プライベート共有
 * サービスたぶん無くならない
 * **外部の issue にリンクを張ることで、リンクバックすることができる**

最後の、仕事の issue に、他人に見えない形でプライベートなメモへのリンクを混ぜ込めるのは、なかなか画期的な気がします。普通のメモアプリだと実現が難しそう。

issue のクローズという一番大きなステータスをどうするといいのかは迷っています。もう更新しないようなメモをクローズしていくといいかもしれません。

Wiki のことは、画像をドラッグドロップできるようになったら考えます。。