OmniAICreator/Qiita-1.07M huggingface dataset:
頭が良い人と頭が悪い人とあたまがわるいひとのアプリ

アドベントカレンダー最終日です。わーい
昨日は [@starcoop](https://qiita.com/starcoop) が [Heroku openで503エラーがでた話](https://qiita.com/starcoop/items/de4515010f1370e092ba) でエラーの原因究明の流れを共有してくれました！

本日はネタアプリ回です。
## できたもの

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">頭がいい人と頭が悪い人とあたまがわるいひとのネタアプリ <a href="https://t.co/DQmATnSIKy">pic.twitter.com/DQmATnSIKy</a></p>&mdash; なかもと (@yashinawa_retai) <a href="https://twitter.com/yashinawa_retai/status/1342382238005043200?ref_src=twsrc%5Etfw">December 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 元ネタ

[頭の悪い人](https://dic.pixiv.net/a/%E9%A0%AD%E3%81%AE%E6%82%AA%E3%81%84%E4%BA%BA)

「物を見て、連想する数が多いほど賢い」とのことでモック的にできそうなのでやってみました。
なお、キャラクターの権利？が怖いので絵文字で代用しました。
## 概要

要素としては

- ウェブアプリ化
    - FastAPI
    - Vue.js
- 物体検出
    - PyTorch
    - VGG16(ImageNet)
- 知識グラフ
    - ConceptNet
- グラフデータベース
    - Neo4j

が登場します。流れとしては

1. フロントから画像をAPIにPOST
2. 画像中の物体を検出
3. 物体名を英語から日本語に変換
4. 日本語の物体名からネットワークを辿って列挙
5. フロントに返して連想させる

くらいです。

## 各要素について

どんなだったかメモしておきます。

### ウェブアプリ化

#### FastAPI

少しつまづいた点としては、アップロードされた画像ファイルを受け取って処理する方法です。
こちらを参考にしました。
[How to convert the uploaded image to Numpy array? #2376](https://github.com/tiangolo/fastapi/issues/2376)

(自分の引用になってしまいますが) FastAPIでモックを作るときは、index.htmlを読み込んでもらうのが楽だと思います。ビルドしたフロントであったりCDNを利用した軽めのものでも楽にできます。
[FastAPIでの静的ファイル, index.htmlの設定(WebSocket Chatの例を直す)](https://qiita.com/sh05_sh05/items/cc37aa9188ec963c8fdd)

#### Vue.js

UIはVuerifyを利用しました。フロントというほどではなくほとんどindex.htmlペライチでVue.js, VuetifyのどちらもCDNを利用しました。

### 物体検出

#### PyTorch

大きな理由はないですが、GPUを使うときの書き方が好きで自分はよくPyTorchを利用しています。

#### VGG16(ImageNet)

今回はpretrained modelを利用しました。
[TORCHVISION.MODELS](https://pytorch.org/docs/stable/torchvision/models.html)

自然言語処理のHuggingFaceもそうですが、Deepなものをパッと動かせるのはとても罪悪感がありますね。
とはいえ、学習時と同じ条件にする必要があるので要注意です。こちらのサイトがいつもとても参考になります。
[PyTorch (7) VGG16 - 人工知能に関する断創録](https://aidiary.hatenablog.com/entry/20180212/1518404395)

### 知識グラフ

連想を辿るために知識グラフを利用しました。初めの案ではMicrosoftのConcept-Graphを検討しましたが、学術利用のみでした。そこでConceptNetを利用することにします。

#### ConceptNet

IsAはもちろんSynonymなど多くの関係が格納されています。[公式ページ](https://conceptnet.io/)で少し遊ぶだけでも楽しいと思います。

WebAPIも公開されていますが、せっかくなのでダウンロードしてデータベースに格納することとしました。GitHubのWikiが充実しています。
[commonsense/conceptnet5](https://github.com/commonsense/conceptnet5/wiki)

また、正確な方法ではないですが、各NodeはLanguageも保持しているのでSynonym(同義)の関係を辿って”banana”(en)から「バナナ」(ja)を取得することができます。後述しますが、うまくいかないこともあります。

### グラフデータベース
 
触ってみたかったのでNeo4jを採用しました。

#### Neo4j

ドキュメントがわかりやすくDocker, Docker-Composeの資料もあります。
[How-To: Run Neo4j in Docker](https://neo4j.com/developer/docker-run-neo4j/)

ConceptNetや先述のConcept-Graphの格納方法も共有されています。
[Non-Text Discovery with ConceptNet as a Neo4j Database [Community Post]](https://neo4j.com/blog/conceptnet-neo4j-database/)

また、Neo4jの管理画面WebUIでは映画に関する関係グラフのチュートリアルがあり、「俳優Aの共演者とは共演した」が「俳優Aの共演者ではない」のような、いわゆる友達の友達のような内容も扱っています。楽しいです。

## おわりに

データセットや事前学習済みモデルなど使いまくりで巨人の肩に乗るどころか騎馬戦をしている気分でした。
が、実は”banana”の日本語への変換で「甘蕉」と選んでしまって関係をあまり辿れなかったり、微調整中なのでコードなども含めて更新予定です。

完走した感想ですが、たくさんの後輩が参加してくれて嬉しかったです、らいねんもがんばってください。
アドベントカレンダー参加者のみなさん、お疲れ様でした！ケーキ食べるぞ〜〜
備忘録(課題:モデルテストコード)

画像ファイル付きのモデルテストコードで忘れていたのでアウトプットします。

テスト用画像をアプリケーションファイルへ配置するためにpublicディレクトリにimagesディレクトリを作成

作成したimagesディレクトリの中に画像を配置する
例：(public/images/テスト用画像ファイル)

```ruby
class Item < ApplicationRecord
  belongs_to :user
  has_one_attached :image

 省略
  validates :image, presence: true
end

```

validatesは、presence: trueで良いみたいでした。


該当のfactoriesディレクトリに画像を開くための記述をする
#### afterメソッド
任意の処理の後に指定の処理を実行することができる
after(:build) とすることで、インスタンスがbuildされた後に指定の処理を実行できる

```ruby
FactoryBot.define do
  factory :item do
    
省略

    after(:build) do |item|
      item.image.attach(io: File.open('public/images/テスト用画像ファイル'), filename: 'テスト用画像ファイル')
    end
  end
end

# io: File.openで設定したパスのファイル（public/images/test_image.png）を、test_image.pngというファイル名で保存

```

テストコードを記述するファイル(今回はitem_spec.rbファイル)での記述方法

```ruby
省略
      it 'nameが空では保存できない' do
        @item.name = ''
        @item.valid?
        expect(@item.errors.full_messages).to include("Name can't be blank")
      end
      it '商品の画像がないと保存できない' do
        @item.image = nil #←ActiveStorageを使用していると""だとエラーが出るため、nilを使って空欄を演出する
        @item.valid?
        expect(@item.errors.full_messages).to include("Image can't be blank")
      end

省略
```


今までは""で統一して実装していたけど今回のテストコードで勉強になりました。
バリデーションの掛け方やテストコードの記述方法は他にもあると思いますが、ひとまずは、テストコードがグリーンになったので機能していると思われます。
railsでアプリケーションを立ち上げたらすること

# はじめに
「Ruby on Rails 5　速習実践ガイド」のアウトプットを行うための投稿です。
業務でrailsを使用しているため、知識をプライベートでも蓄えようと思い取り組んでます。
今回は、本書のアプリケーションを立ち上げ後の設定関連を投稿します。

# 目次
1. [slimファイルに変換](#Chapter2)
1. [bootstrapの導入](#Chapter3)
1. [scssファイルに変換](#Chapter4)
1. [エラーメッセージの日本語化](#Chapter5)
1. [モデルのカラム名を日本語化](#Chapter6)
1. [参考文献](#reference)

<a id="#Chapter2"></a>
# slimファイルに変換
Gemfiileの最終行に、以下を追加

```Gemfile
gem 'slim-rails'
gem 'html2slim'
```

記述後ターミナルで以下を実行

```
bundle install
bundle exec erb2slim app/views/layouts/ -d
```

2行目のコマンドで、「app/views/layouts/」以下のファイルをerbをもとに、slimファイルを作成してくれます。オプション「-d」は、htmlファイルを削除するオプションです。もし、htmlファイルを残しておきたい場合は、こちらのオプションを付けません。

<a id="#Chapter3"></a>
# bootstrapの導入
Gemfiileの最終行に、以下を追加

```Gemfile
gem 'bootstrap'
```

記述後ターミナルで以下を実行

```
bundle install
```

<a id="#Chapter4"></a>
# scssファイルに変換

app/assets/stylesheets/application.cssを削除し、同じフォルダにapplication.scssを作成し、ファイルに以下の記述を追記します。

```app/assets/stylesheets/application.scss
@import 'bootstrap'
```
<a id="#Chapter5"></a>
# エラーメッセージの日本語化
GitHubのrails-I18nリポジトリに翻訳されたものがあるのでそちらを以下のコマンドでダウンロードします。

```terminal
wget https://raw.githubusercontent.com/svenfuchs/rails-i18n/master/rails/locale/fa.yml -P config/locales/
```

ダウンロード後、デフォルトで日本語コンテンツを利用するように、locale.rbを作成し、以下の記述を追記します。

```config/initializers/locale.rb
Rails.application.config.i18n.default_locale = :ja
```

<a id="#Chapter6"></a>
# モデルのカラム名を日本語化
画面にモデル名を表示するとテーブルのカラム名がそのまま出力されてしまいます。
（例：User.nameのときは、nameと表示されてしまいます）
そこで、ja.ymlにモデル名とカラム名に対応する日本語を割り当てます。なお、インデントがずれるとうまくいかないので、注意です！

```config/locales/ja.yml
---
ja:
  activerecord:
    errors:
      messages:
        record_invalid: 'バリデーションに失敗しました: %{errors}'
        restrict_dependent_destroy:
          has_one: "%{record}が存在しているので削除できません"
          has_many: "%{record}が存在しているので削除できません"
    models:
      task: タスク
    attributes:
      task:
        id: ID
        name: 名称
        description: 詳しい説明
        created_at: 登録日時
        updated_at: 更新日時
```

定義後、ビューでhuman_attribute_nameメソッドを使って日本語表示をします。

```
Task.human_attribute_name(:name)
```

この記述で、「名称」と表示されます。
<a id="#reference"></a>
# 参考文献
- [現場で使える Ruby on Rails 5速習実践ガイド](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4-ebook/dp/B07JHQ9B5T)
【AWS】[Route53]ドメイン登録後、デフォルトのホストゾーンを削除してしまった場合の復旧

AWS初心者で、Route53ドメイン登録後、デフォルトのホストゾーンを削除してしまった。
とたんに今までのドメインが使用不可に。
慌ててホストゾーンを再作成したが一向にDNSエラーが解消せずかなり焦った。

落ちとしては登録済みドメインのネームサーバを更新していなかったため。

復旧までの手順を備忘として残します。

1.ホストゾーンの再生成
---
登録したドメインと同じ内容で、ホストゾーンを再生成します。
※すでに再生成している場合は手順２へスキップ。
![aws1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/980600/dbb15739-8431-dd7a-d026-8c3a100097a9.png)


2.作成されたホストゾーンのNS(ネームサーバ）4つをメモなどにコピー
---
作成されたホストゾーンのネームサーバをメモ帳に保存します。
![aws2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/980600/589947fa-680a-af25-45d4-da58a9ed8148.png)


3.登録済みドメインのネームサーバを変更する
---
登録済みドメインから、対象のドメインを選択し、ネームサーバを編集する。
手順２でコピーしたネームサーバを4つ上書きする。
※末尾の「.(ピリオド)」は外す
![aws3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/980600/e4e1049d-9749-3708-70da-a3dcf2ddb375.png)


4.あとは時が過ぎるのを待つ
---
キャッシュの関係上即反映はされず、約72時間後に復旧する。
※私の場合は36時間くらい

以上です。
Zabbix用のMySQLデータディレクトリの移動について

Zabbix Advent Calendar 2020の25日目です。

今日は、先日のZabbix Conference Japan 2020の最後に質問として挙がっていたことに対する対応手順の紹介です。

# 経緯

Zabbixを使い始めたは良いけれど、サイジングへの考慮が不足していたり監視するホストや監視項目の急増などによって、MySQLのデータのサイズが膨れ上がってしまってディスクの空き領域が足らなくなってしまったなんてことになったことはありませんか？
急遽、新しいハードディスクを接続して、そのハードディスクに移動したらZabbixからアクセスできなくなって困ったりしませんでしたか？

そんなときの為に、どのような設定が必要であるのかをご紹介しようと思います。


# 環境の想定

今回は、以下のような環境での手順を紹介します。

* OS： CentOS 8.3.2011
* Zabbix： 5.0.7
* MySQL： 8.0.21 (CentOS 8標準)
* PHP： 7.2.24 (CentOS 8標準)

標準のパッケージのデフォルトでインストールすると、MySQLのデータディレクトリは、/var/lib/mysqlになります。
これを、/mnt/dataに新しくマウントしたディスク上に移動することとします。
/var/lib/mysqlディレクトリ自体を移動するとなると、単純にデータだけではなく、ローカルからアクセスする際に使用するソケットファイルも移動することになります。

この移動を行って正常に稼働させるためには、以下の設定ファイルを変更する必要があります。

* MySQL (サーバー側、クライアント側の両方)
* PHP
* Zabbixサーバー

詳細な手順は、以下で説明します。


# 移動手順

## Zabbbixのインストール

CentOS 8上にバックエンドとしてMySQLを使用してZabbix 5.0の環境を構築する手順としては、以下のリンク先を参照してください。

* [Zabbix 5.0をCentOS 8にインストール(MySQL編)](https://qiita.com/atanaka7/items/282a3499e135e00edcdc)


## 各サービスの停止

使用している各サービスを停止します。

```
# systemctl stop httpd php-fpm zabbix-server zabbix-agent
```

Zabbixエージェント2を使用している場合は、「zabbix-agent」の代わりに「zabbix-agent2」のサービスを停止してください。


## MySQLのデータディレクトリの移動

MySQLのサーバーデーモンを停止します。

```
# systemctl stop mysqld
```

新しいディスクを/mnt/dataにマウントします。正常にマウントできたら、MySQLのデータディレクトリを移動します。
安全のためには、正常に移行できるまでは取っておいて正常に移行できたら移行元のディレクトリを削除するようにした方が良いと思います。

ここでは再帰的にコピーして、移行元のディレクトリの名前を変更して誤ってアクセスしないようにしてみます。

```
# cp -pr /var/lib/mysql /mnt/data
# mv /var/lib/mysql /var/lib/mysql.org
```

MySQLの設定ファイルとして変更すべきなのは以下の2つのファイルです。

* /etc/my.cnf.d/mysql-server.cnf
* /etc/my.cnf.d/client.cnf

それぞれの修正内容は以下の通りです。


``` /etc/my.cnf.d/mysql-server.cnf
[mysqld]
datadir=/mnt/data/mysql
socket=/mnt/data/mysql/mysql.sock
mysqlx_socket=/mnt/data/mysql/mysqlx.sock
```

``` /etc/my.cnf.d/client.cnf
[client]
socket=/mnt/data/mysql/mysql.sock
```

設定を変更したら、MySQLサーバーを起動してmysqlコマンドでアクセスできるか確認してみてください。

```
# systemctl start mysqld
# mysql -uroot -p
```


## PHP用の設定変更

PHPからローカルのMySQLサーバーにアクセスするときには、明示的に指定しないと各種プラグインのデフォルトのソケットのパスを使用してアクセスしようとします。
CentOS 8のPHPのデフォルトは、/var/lib/mysql/mysql.sockになっているので、ディレクトリを移動した場合は、明示的にPHPにも設定することが必要です。

Zabbixを使うだけであれば、mysqli用のソケットファイルのパスを指定するだけでよさそうです。

具体的には、/etc/php.ini内に以下のように設定します。


``` /etc/php.ini
mysqli.default_socket = /mnt/data/mysql/mysql.sock
```

## Zabbixサーバー用の設定変更

Zabbixサーバーは、設定ファイル内のDBSocketを設定します。

``` /etc/zabbix/zabbix_server.conf
DBSocket=/mnt/data/mysql/mysql.sock
```


## Zabbix関連のサービス起動

ここまで設定すれば、あとは各サービスを起動するだけです。

```
# systemctl start zabbix-agent zabbix-server php-fpm httpd
```

Webインターフェースにアクセスして正常に情報を参照できるかを確認してください。
Zabbixサーバーのログも異常が発生していないかを確認しておくと良いでしょう。


# 最後に

私自身も正確で具体的な手順を持っていたわけではなく、今回改めて、特定のOSやミドルウェアのバージョンだけでの確認となっていますが、異なるディストリビューションやバージョンでも考え方は利用できると思いますので、同様のことをしたいと思ったら、この投稿を思い出していただければと思います。

今回ご紹介した方法以外にも、新しいディスクを追加して新しいマウントポイントにマウントして使うのではなく、LVMを使用していればボリュームに追加して領域を拡張するような対応策もあるので、興味のある方は調べてみてください。

今後も是非Zabbixをご活用ください。

それでは、皆様良いお年をお迎えください。
デュアルブートでUbuntu18.04.5を構築

WindowsとUbuntuが既に入っているPCに、さらに別バージョンのUbuntuをインストールしてみる。

# イメージのダウンロード
http://ftp.riken.go.jp/Linux/ubuntu-releases/18.04.5/
理研からDL。

# USBメモリにイメージを入れる
https://ja.osdn.net/projects/sfnet_unetbootin/
を利用。

# ブート
起動時にF12で入る。
USBを選択。

# インストール
既存のOSと共存させたかったので、「その他」を選択。
パーティションの作成がひと手間いる。
参考：http://lab.cntl.kyutech.ac.jp/~nishida/sphinx/post/2015.08.28-dualboot-ubuntu.html

作成するのは以下3つ。

* efiのブート領域：2GB
* ext4の「/」領域：100GB
* swap領域：32GB（物理メモリと同じ大きさに）

インストールするデバイスは、作成した「efi」を選択。

# グラフィックカードの認識
GTX1080が搭載されているはずだが、認識されていない。
https://qiita.com/yto1292/items/463e054943f3076f36cc
CUDAドライバーを入れると認識されるようになる。

```
$ sudo apt-get install cuda-drivers
```
COVID-19のオープンデータを検証するというお話

この記事は[Civictech１年目 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/civictech_rookies)の最終日(25日目)の記事です。(とは言ったものの、書き溜めしていた記事を消化したもので、あまり面白い記事ではないです...)

どうも、兵庫県版コロナウイルスまとめサイトコントリビュータ、y-chanです。本家東京都サイトにも積極的にコントリビュートしたり、SecHack365に受かっていろいろ開発したり、[CCC2020](https://ccc2020.code4japan.org)の運営をしていたりと大忙しな感じです。
今回は、COVID-19のオープンデータを検証するというような、おそらくどこもやってない(当社調べ)ことをやったのでそれについてちょっと書き残しておこうかなといった感じです。聞きかじった話が含まれてるので間違ってる部分もあるかと思いますが、編集リクエストなんかで優しく直してくれるとありがたいです。

# 私たちはデータラングリングをしている
前置き的なお話をしたいと思います。私たちがやっている、オープンデータを取得したり、整形したり、あるいは加工したりという行為はデータラングリングというそうです。データラングリングという言葉自体は造語のようですが、ラングリングは「飼い慣らす」などの意味があるそうです。ちなみに私は今までデータを触ることなどなかったので、この用語自体つい最近まで知りませんでした。

# オープンデータを検証する？何言ってんだこいつ
見出しの通りです。普通、オープンデータのようなものを利用する場合、検証するというのはおかしな話です。オープンデータは政府や自治体によって公開されるもので、データの入力は人間が行っているであろうデータです。当然、ヒューマンエラーがありえます。完璧であることはほとんどありません。そのようなエラーやノイズ、欠損値はオープンデータの利用者自身が「前処理」といった段階でデータの間違いを正規化したり、データを消去(ないものとして扱う)したりすることでデータを正して利用するものです。なお、この「前処理」もデータラングリングに含まれる行為のようです。しかし、データラングリングにおいて「検証」という行為は普通ありませんし、しないものとされています。

COVID-19のオープンデータをデータラングリングするにあたって検証すると言って、「は？」となった方も多いのではないでしょうか。私もまとめサイトのチーム内で「検証機構作ってます」というと、とある方に「検証するってどういうこと？」というような反応をされました。「検証よりも前処理すべき」と。前処理も必要ですし、いくつかしているんですが、各地の新型コロナウイルス感染症対策サイトはオープンデータについて自治体と連携を行っているところも多いと思います。兵庫県版では特に連携はしていないのですが、以前に「データ間違ってるんじゃない？」と指摘を入れたことがあります。指摘を入れた理由はただ単に私の整形するデータに狂いが生じるからなのですが、陽性患者の属性のようなセンシティブなデータは正しい形であるべきだと思っていた節もあります。その頃はデータの数も少なく、ヒューマンエラーもほんのちょっとだけだったのですが、今となっては感染者数が増大し、ヒューマンエラーの数もかなり増えています。同時に、ミスを見つけるのも難しくなります。じゃあ、ミスの発見はプログラムに任せようと私は考えたわけです。これがオープンデータを検証しようとしたきっかけです。まあ、データの修正を県の担当者に押し付けるのは何か違うような気もしますが...

# オープンデータの検証方法
では、オープンデータの検証はどのように行ったのか、軽く書き残しておきたいと思います。検証、といってもやっていることは単純明快で

- データが文字列である場合、定型に当てはまったものであるか
- データが数値で、複数の形式(日別や累計など)で公開されている場合、その数値は整合性が取れているか(簡単に言えば、日別の値の合計が累計の値と一致しているかなど)

のみを見ています。
実際、誤字脱字などのミスは修正可能ですが、数値の誤差などはこちら側で正確に修正する、ということはできず、そのまま使うか切り捨てるしかないため、まとめサイト上では結局修正されずに元データがほぼそのまま掲載されています...
また、文字列に関してはルールが定められているかはわからず、今まで出てきたデータを基に定型を設定しているので、たまに例外的なのが検証に引っかかることもあります。
ちなみにスクリプトは[データスクレイピングのものと同一のもの](https://github.com/stop-covid19-hyogo/covid19-scraping)です。

# 検証結果について
検証結果は[誰でも閲覧できるようにしています](https://warnings.stop-covid19-hyogo.org)。検証の結果、データに間違いのある可能性がある場合、その節をメッセージ化して表示するようなスクリプトを組みました。とはいえ、突貫工事感が否めなくて、メッセージの意味がいまいちわかりずらいという問題は残っています...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535794/54f65c23-2a4d-91bf-2de6-9ffeb9cc5835.png)
また、あくまでこれは私が設定した基準での検証結果となります。数値に関しての差は明らかにミスであるとうかがえるものの、定型に当てはまっていない文字列はヒューマンエラーといえるかは微妙です。この辺りも普通はオープンデータの検証などをしない理由でしょう。

# まとめ
1. オープンデータを利用者側が検証するという謎行為をした
2. 検証結果を誰でも見れるようにした
3. 突貫工事すぎて検証がわかりにくい
4. まだしていないが、検証結果を基に前処理もできるようにしたい。

意味の分からないまとめになりましたが、"4"をいまだにやってないのはちょっとやばいかなと思っています。ただ、センシティブなデータであるということもあり、数値を勝手に変えたり、陽性患者の属性情報を書き換えたりするのはどうなのかなとも思いつつあります。そこらへんが、COVID-19というオープンデータの難しさなのではないでしょうか。
C#にパイプライン演算子もどきの拡張メソッドを作って書き味を向上させる

# 概要

C#にはパイプライン演算子が導入されていません。しかし拡張メソッドを用いることで似た書き味を実現することができます。

この記事では最初に使用例を提示し、どのような書き味が実現されたか提示します。
次にパイプライン演算子とはどのようなもので、どのような要望に応える演算子かを説明します。
そしてパイプライン演算子もどきの拡張メソッドをどのように定義するか説明します。
最後に雑感を述べてから今回使用したコード全体を掲載します。

# 使用例
以下は自前で定義したPipe拡張メソッドを用いたFizzBuzzの例です。

```csharp
static void Main(string[] args) =>
    Enumerable.Range(1, 30)
        .Select(
            n =>
                n % 15 == 0 ? "FizzBuzz":
                n % 3 == 0 ? "Fizz":
                n % 5 == 0 ? "Buzz":
                n.ToString())
        .Pipe(l => string.Join("\n", l))
        .Pipe(Console.WriteLine); 
```

- 1から30の数字に対して
- それぞれFizzBuzz変換を施して
- 文字列のリストを改行で連結して
- 最後にWriteLineする

と言う操作が、その順番通りに書けています。
記述量もそれほど多くなく、まあまあすっきりと書けているのではないでしょうか。

# パイプライン演算子とは

F# や OCamlなどに採用されている演算子です。
今、「値vを関数funcFに渡して、返り値をその次に関数funcGに渡して、その返り値を関数funcHに渡して、その返り値を関数funcIに渡す」ことをしたいとします。素朴に書けば`funcI(funcH(funcG(funcF(v))))`ですが、

- 実行順と関数を書く順が逆になっていて直感的でない
- 関数のネストが深くなっている

などにとっつきづらさを感じる人もいるかと思われます。
これを解決して、「vをfuncFして、funcGして、funcHして、funcIする」をその順番通り書くためには

```csharp
var fedValue = funcF(v);
var gedValue = funcG(fedValue);
var hedValue = funcH(gedValue);
funcI(hedValue);
```

などと書くのも手です。しかし

- 命名が面倒
- 記述量が増える

などのトレードオフがあります。

パイプライン演算子 `|>` を持つ上述の言語たちでは、「vをfuncFして、funcGして、funcHして、funcIする」を以下のように言葉の順番通りに、かつシンプルに書くことができます。

```
v
|> funcF
|> funcG
|> funcH
|> funcI
```

# パイプライン演算子もどき拡張メソッドの定義

今回はPipeと言う名前で、以下のように定義しました。

```csharp
static class PipeExtensions {
   public static B Pipe<A, B>(this A data, Func<A, B> f) => f(data);
   public static void Pipe<A>(this A data, Action<A> f) => f(data);
}
```

# 雑感

## Pipe拡張メソッドを定義する是非について

全ての型に対して見かけ上のメソッドを生やすものであるため、個人開発のプロダクトならまだしも複数人で開発するときに個人の判断でこんなメソッドをほいほい生やすのはためらわれますね...
標準ライブラリに梱包してくれれば安心して使えるのですが。

## 定義の仕方について

返り値がvoid以外の場合と、voidの場合とで定義を分けなければならないのは億劫ですよね...

## 書き味の向上について

今回定義したPipe拡張メソッドはまあまあ便利だと思っています。
今回用いた例で言うと「得られたデータを最後にWriteLineする」を`.Pipe(Console.WriteLine))`で表せているのはだいぶすっきりとした印象を受けます。
しかし、「listをJoinする」部分が`.Pipe(l => String.Join("\n", l))`となっているのは若干冗長な気がしないでもありません。
OCaml的なCurry化や部分適用の厚い構文上のサポートがあったら`.Pipe(String.Json "\n")`で済ませられそうですし、
Scala的なプレースホルダの記法があれば`.Pipe(String.Json("\n", _))` みたいな書き方で済ませられそうです。
そういう構文上のサポートがない以上、C#においては今回のパイプライン演算子もどきの拡張メソッドを定義しても嬉しさが少し減っていそうです。

比較

```csharp
.Pipe(l => String.Join("\n", l)) // 現行
.Pipe(String.Join "\n") // こうとか
.Pipe(String.Join("\n", _)) // こうとか書けたらもっと楽だなあという妄想
```

# コード全体

今回用いたコードは以下の通りです。そのままOnline Compilerとかに貼り付ければ動きます。

```csharp
using System;
using System.Linq;

namespace FizzBuzz
{
    class Program
    {
        static void Main(string[] args) =>
            Enumerable.Range(1, 30)
                .Select(
                    n =>
                        n % 15 == 0 ? "FizzBuzz":
                        n % 3 == 0 ? "Fizz":
                        n % 5 == 0 ? "Buzz":
                        n.ToString())
                .Pipe(l => string.Join("\n", l))
                .Pipe(Console.WriteLine); 
    }
    
    static class PipeExtensions {
       public static B Pipe<A, B>(this A data, Func<A, B> f) => f(data);
       public static void Pipe<A>(this A data, Action<A> f) => f(data);
    }
}
```
【jQuery】プラグイン「FlexSlider」を使ってみた


ECサイトとかでよくある、「TOPの大きな画像が次々スライドされていくやつ」を実装したくて探していたところ、「FlexSlider」というプラグインがあることを発見しました:eyes:

よく使われていて尚且つ簡単！みたいなので私もやってみました。
jQuery超初心者ですが、なんとか実装できたので、まとめておきます！


## 1.「FlexSlider」をダウンロード
まずはFlexSliderを下記よりダウンロードします。

[FlexSlider](http://flexslider.woothemes.com/)

ダウンロードできたら解凍して、
中に入っているファイルたちをボコーン！と自分の環境に入れます。

## 2.`<head>`タグ内にjQueryを読み込ませる

`<head>～<head>`タグ内に下記を入れます。
**JQueryの読込も忘れずに！！**

```HTML:HTML
<link rel="stylesheet" href="flexslider.css" type="text/css" />
<script src="jquery.js"></script>
<script src="jquery.flexslider.js"></script>
```

ちなみに私はjQuery読込したはいいけど、自分のサイトはhttps://なのに
jQueryはhttp://で読み込んでたので、
エラーで全くスライダーが表示されず、1時間くらいをドブに捨てました:v:

これも勉強だと思えば・・・（涙目）


## 3.スライダーにしたい画像を入れる

```HTML:HTML
<div class="flexslider">
<ul class="slides">
<li><img src="画像1.jpg" /></li>
<li><img src="画像2.jpg" /></li>
<li><img src="画像3.jpg" /></li>
<li><img src="画像4.jpg" /></li>
</ul>
</div>
```


## 4.JSを入力

```Javascript:Javascript
<script type="text/javascript" charset="utf-8">
$(window).load(function() {
$('.flexslider').flexslider();
});
</script>
```

ここの部分については、[FlexSlider](http://flexslider.woothemes.com/)のサイトに載ってるスライダーの見本から
自分が実装したいJSを引っ張ってくればOKです！！


## まとめ
参考サイト様
[【jQuery】超万能スライダー FlexSlider の使い方をマスターする。](https://on-ze.com/archives/409)
こちらにカスタマイズする際のオプションがたくさん載っています:heart_eyes:

私もひとまず実装はできたので、後は自分の作ったサイト用にカスタマイズしていきますが・・・難しそう！！けど頑張る！！
Express Router

# はじめに
ExpressのRouterオブジェクトについて調べたのでまとめました。

## 開発環境
OS : Windows10
エディター : VSCode
Express Version :  4.17

## ファイル構成
```
>tree
.
|-app.js
|-route
    |-event.js
```

## Express Router
expressのルーティング処理を行うオブジェクトです。
GETやPOSTなどのリクエストに対して簡単にレスポンスを記述できます。
routerオブジェクトにはミドルウェアの設定やHTTPメソッドに対するルート処理を記述できます。

```javascript:app.js
const router = require("./route/event");
const app = express();

// routerを読み込む
// /events以下はevent.jsに任せる
app.use("/events", require("./route/event.js"));

app.listen(3000);
```

```javascript:route/event.js
const router = require("express").Router();

router.get("/", (req, res) => {
  res.send("/evnets/");
});

module.exports = router;  // 外部から読み込むために
```

http://localhost:3000/events にアクセスすると/events/が表示されます。

## Router メソッド
Routerオブジェクトのメソッドを少し調べたので、下にまとめます。

### router.all(path, [callback, ...] callback)
指定したパスへのアクセスに足してコールバックを呼び出します。認証が必要な際に利用できます。

次のコードはアクセスされた際に認証を行い、その後にユーザー情報を読み込みます。
下のコード以降に記述したルートに対して有効になります。

```javascript

router.all('*', requireAuthentication, loadUser)
```

### router.METHOD(path, [callback, ...] callback)
GET, POST, PUTなどのHTTPメソッドに対してルーティングを設定できます。
pathには正規表現も使えます。

```javascript

router.get('/', function (req, res) {
  res.send('hello world')
})
```

### router.param()
ルートパラメーターをもとにコールバック関数を呼び出すかどうか設定できます。

```javascript
const router = require("express").Router();

// idというルートパラメータが使われる際に処理を行う。
// 今回はhttp://localhost:3000/3 アクセスをしてみる
router.param("id", (req, res, next, id) => {
  console.log("id : " + id); // id : 3
  next();
});

// idをルートパラメータとする
router.get("/:id", (req, res) => {
  res.send("/evnets/");
});

module.exports = router;
```

### router.route(path)
routerインスタンスを生成します。
複数のHTTPメソッドに対するルートを定義する際に使えます。
下の例ではGET、PUTにはアクセス可能で、POST、DELETEではエラーが発生します。

```javascript
router.route('/users/:user_id')
  .all(function (req, res, next) {
    // ここにコールバック関数を定義すれば、
    // 各HTTPメソッドに対する処理の前に呼ばれます。
    next()
  })
  .get(function (req, res, next) {
    res.json(req.user)
  })
  .put(function (req, res, next) {
    // 更新処理
    res.json(req.user)
  })
  .post(function (req, res, next) {
    next(new Error('not implemented'))
  })
  .delete(function (req, res, next) {
    next(new Error('not implemented'))
  })
```

### router.use([path],[function, ...] function)
app.useと似たような処理を行います。
router.useではサブパスに対してルーティング設定を行えます。

```javascript
var router = express.Router();

app.use('/first', router); // Mount the router as middleware at path /first

router.get('/sud', smaller);  // first/subに対するアクセスはsmallerルーターに処理を任せます。

router.get('/user', bigger);  // first/userに対するアクセスはbiggerルーターに処理を任せます。
```

## おわりに
router.METHODについては使用したことがあったのですが、その他はあまり使ったことがなかったので勉強になりました。
次はEJS、gulp, eslintについて調べようと思います。
null byteエラーが発生したときの対処

# やりたいこと

①S3から複数のZipファイルを取得

②複数のzipファイルを解凍しディレクトリ形式にする

③最終的に1つのzipファイルにしたい。

# 起きた問題

①でget_objectしたところ、以下のようになってしまい、②で必要なファイルパスが取得できない状況。

`PK\u0003\u0004\u0014\u0000\u0000\u0000\u0000\u0000\xA1\x9AYQ\u0000\u0000\u0000`

```ruby
client = Aws::S3::Client.new(
	:region => 'ap-northeast-1',
	:access_key_id => '***',
	:secret_access_key => '***',
)      
@orders.each do |order|
	obj = client.get_object(
	bucket: "*",
	key: order.zip_file_name
	).body.read  
    
	Rails.logger.debug obj.inspect
    #=> PK\u0003\u0004\u0014\u0000\u00....
end
```

# 原因

zipファイルの中身はバイナリデータ。

ダウンロードしてきたファイルの中身をobjに参照させている。`.body.read`という関数は、zipファイルの中身を参照する関数。なので、当然バイナリがログで出てくる。

# 解決策

readせずに、rubyzipの`open_buffer`メソッドを使って、zipファイルを開く。
ただ、open_bufferメソッドはrubyzipの公式ドキュメントには載っていない。。。
もっと良いやり方見つけたい。

```ruby
client = Aws::S3::Client.new(
	:region => 'ap-northeast-1',
	:access_key_id => '***',
	:secret_access_key => '***',
)      
@orders.each do |order|
	obj = client.get_object(
	bucket: "*",
	key: order.zip_file_name
	).body  >> readしない！    
end

# get_objectでopenする
Zip::File.open_buffer(obj) do |zip|
	~~
end
```
ヒーローズリーグオンライン2020で個人スポンサーをやってみた

# 一番強く感じたこと
###「喜んでもらえるとあげるほうも嬉しくなる」
[『ヒーローズリーグオンライン授賞式＆記念撮影会』](https://youtu.be/8ePSxVdohHA) で、運営側からの説明スライドでも何度か出ていましたが、この言葉でほどんどが語られると思います。個人スポンサーをやってみて、本当に良い経験が出来たと思います。ということで、ちょっと個人的な感想を書きながらまとめとします。

# 個人スポンサーになろうと思ったきっかけ
特に立派な理由がある訳ではないです(笑)
以前から、アイデアソンやハッカソンに参加したり、コンテストやモノづくり系イベントに参加したり、有志でサークル活動的にいろいろやってみたりしてたのですが。その流れで、2019年は[ROHM主催のコンテスト](http://open.rohm.com/jp/events/rohc2019_190930) で最優秀賞を貰ったり、そのままCEATECで出展させてもらったり、HL2019MAリーグ決勝戦に参加したり、[その他の作品](https://protopedia.net/prototype/1406)でも決勝戦にエントリーさせて貰ったり、いろいろ経験させてもらった中で、MAの運営メンバーの方々にはいろいろお世話になってるなぁと思うところがきっかけとして大きかったと思います。
あとは、コンテストなどに参加して賞金を頂く機会もそれなりにあったので、その賞金を何かしらの形で次の人に回していく機会になるのかなとも思いました。こうやってモノをつくる仲間が増えていくことは楽しいことだと思ってます。

#### 一瞬の躊躇
個人スポンサーを募集していることを知り、やはり最初は少し考えました。個人スポンサーの費用としては、コンテストなどの賞金を当てればそんなものかと思ったのですが、一番の悩みは「自分なんかがいいのかなぁ」ということでした。自分よりももっと良い作品を作ってる方々や、過去のヒーローの方々とか、もっと活動を積極的にやっている方などなど。
それでも、いろいろ経験させてもらっていることを踏まえて、次に新しく作品を作ってチャレンジしてみる人に何か新しいきっかけが提供できたらいいなと思い、個人スポンサーを引き受けることにしました。
＊個人スポンサーについては詳細は[こちら](https://we-are-ma.jp/news/hl2020-partner/)


# 個人スポンサーの流れ
1. とにかく申し込み
そういえば、申込締切も１日遅れたような気もするのですが、そのあたりさすが！普通にあっさり受け入れてもらいました(笑)

1. スポンサー費用の振込
速やかにささっと振り込みます

1. スポンサー動画の提供
突然、審査基準などを伝える為の動画を要求されました(笑) あまり動画撮影とかに慣れてない上に、全然勝手が分からないので、こっそり他のスポンサーの方の動画を見ながら、自分の動画にフィードバックしたりしてました。とりあえず提出！

1. 全作品が出揃うまで待つ
とはいえ、自分も提出作品を作ったり、少しずつ [ProtoPedia](https://protopedia.net/) にアップされる作品を見たり。

1. 作品を見る
選考基準によると思いますが、比較的対象が広くなるテーマ設定であったので、全作品の動画を見ました（作品数が多かったので、自分を褒めてあげたい）。あとは、個別のオンライン決勝戦もあったりしたので、それも見ながらなんとなく全体の作品を把握するようにしました。

1. 受賞作品の決定
全作品が多いので、一旦、候補リストを作成して絞り込み、更にその中から絞り込んで決定。という流れ。
この候補リストからヒーローズビンゴに選ばれる作品も多かったです。そして、ビンゴも当たりました！

1. 授賞式に参加
「90秒でコメントを」と言われていたので、送る側に失礼がない程度にちゃんと考えながら、伝えたいことは事前に整理。ちゃんと伝わったかは別(笑)


# 個人スポンサーをやってみた感想
####個人賞を送ってみて
ヒーローズリーグで、特に今年の場合のオンラインリーグだと、なんとなく選ばれる作品の傾向とか雰囲気ってあると思っていて、そこにはノミネートはされないんだろうなぁとなんとなく分かる作品でも個人的に良いなと思う作品は結構多くて、そういった意味で、個人賞を出せたのは良かったと思います。あとは、「多くの人に賞を！」というコンセプトのもと、他の人と重なっていない作品に賞を上げられたは良かったと思います。
あとは、冒頭にも書いたように **「喜んでもらえるとあげるほうも嬉しくなる」**がまさにそれ。今回、個人賞を贈らせて頂いた方が喜んでくれて（私にはそう見えてｗ）、とても良かったと思います。

####審査員をやってみて
いままでは基本的に作品を作る側・審査される側だったので、審査する側の気持ちが少し分かったのが経験として良かったです。個人的な意見ですが、この手のコンテストは絶対的な正解的な作品はなく、そのコンテストの審査基準や審査員の好みによって選ばれると思っていて、なので、こちらのコンテストはダメでも、こちらのコンテストは賞が貰えるみたいなことがあると思っています。そういった観点では、個人賞はまさに自分の審査基準で選ぶので、まさにその傾向は出やすいのかなと思いました。やはり、良い意味で審査員の個人的な好みは出ると思います（技術重視／面白さ重視／社会課題重視などなど）。

# 個人賞を送った作品
作品：[うっかりを防ぐスマートな置き台 「SUKKARI」(すっかり)](https://protopedia.net/prototype/2063)
私のコメント：
『うっかり』という、ついついやってしまう身近な課題に着目した作品であり、同じような想いを抱いている人も多いかなと思います。また、「やってしまったー」という人間のネガティブな内面にも着目して、うっかりを防ぐことで、「小さな成功体験」や「心のゆとり」を提供するという壮大なストーリーが素敵です！

受賞者コメントもあるので、もし良かったら。
[Youtube(ヒーローズリーグ オンライン授賞式&記念撮影会)](https://youtu.be/8ePSxVdohHA?t=2096)
[0:34:56]あたりです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21408/e8cb8d4c-00cd-b7f2-223f-96b9f10ad2d3.png)

# 最後に
来年がどうなるかは誰にも分かりませんが(笑)
「個人スポンサーをやれば良かった～」みたいな声も聞こえてきたりしたので、もし機会があって、どうしようかなと思う人の参考になればと思って書きました。次に何かをやろうと思う誰かの参考になればと思います。
学内/学外からの多段sshをMatchオプションで判別し、共通のホスト名でアクセスできるようにする方法


# はじめに

筆者は今まで
「学内からはProxyCommandなしで入れるけど、学外からは中継サーバーを経由しなければいけないのでProxyCommandが必須！」
という状況で、かつ学内から中継サーバーにアクセスすることができなかったので、`~/.ssh/config`の中身を別のホストとして分けて書いていました。

- 例（hogeが目的サーバー、hogehogeが中継サーバー）

```ssh
Host hoge-in
  HostName                XXX.XXX.XXX.XXX
  IdentityFile            ~/.ssh/id_hoge
  User                    hoge

Host hoge-ex
  HostName                XXX.XXX.XXX.XXX
  IdentityFile            ~/.ssh/id_hoge
  User                    hoge
  ProxyCommand            ssh -XC -W %h:%p hogehoge

Host hogehoge
  HostName                YYY.YYY.YYY.YYY
  IdentityFile            ~/.ssh/id_hoge
  User                    hoge
```

このようにすると、いる場所によって使用するホスト名が変わります。
これを回避し、共通のホスト名でsshできるようにするやり方を見つけましたのでメモします。

# 環境

- MacOS 10.15.7 Catalina

```shell-session
 ❯ ssh -V

OpenSSH_8.1p1, LibreSSL 2.7.3
```


# やり方

`~/.ssh/config`において、Matchオプションを用いて判別をしてあげれば良いです。

```ssh

Host hoge
  HostName                XXX.XXX.XXX.XXX
  IdentityFile            ~/.ssh/id_hoge
  User                    hoge

Match originalhost hoge exec "! ping -t 1 -c 1 ZZZ.ZZZ.ZZZ.ZZZ > /dev/null"
  ProxyCommand            ssh -XC -W %h:%p hogehoge


Host hogehoge
  HostName                YYY.YYY.YYY.YYY
  IdentityFile            ~/.ssh/id_hoge
  User                    hoge
```

ここで`originalhost`はコマンドラインで入力したhostnameに対応します。
つまり、

```shell-session
 ❯ ssh hoge
```

としたときの`hoge`に対応します。
また、`ZZZ.ZZZ.ZZZ.ZZZ`は学内サーバーのみからアクセスすることのできるサーバーを指定します。

よって、これらは何をやっているかというと、

`Match`以下の内容は

- `originalhost = hoge`

かつ

- `ZZZ.ZZZ.ZZZ.ZZZ`に`ping`を飛ばせない状況である

これらを満たすとき、以下の内容（今の場合は`ProxyCommand`）を実行せよ

ということです。
こうすることによって、学内/学外によらず

```shell-session
 ❯ ssh hoge
```

でアクセスすることができます。

# 注意・メモ

- このやり方はsshアクセスのたびに`ping`を飛ばすということに注意してください。
- ダブルクォーテーションの中身は好きなコマンドを実行することができます。
（例えば[ssh configでの条件分岐](https://bulbulpaul.com/blog/posts/2017/03/ssh-config-macth/)ではWi-FiのSSIDをチェックして、特定のSSIDのときには違うホストにアクセスするということをやっています。）
- `Match`文は順番に依存します！基本的に上から下に実行されることに注意してください。
（これをうまく使えば、特定の条件のときだけパラメーターを上書きするといったことも可能です。）




# 参考

- [MatchでSSH/SCP接続時のProxyCommand有無を区別する](https://dev.classmethod.jp/articles/ssh-config-match-switches-proxycommand-or-none/)
- [現在のネットワークに基づいてsshIPを変更する](https://www.it-swarm-ja.tech/ja/ssh/%E7%8F%BE%E5%9C%A8%E3%81%AE%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AB%E5%9F%BA%E3%81%A5%E3%81%84%E3%81%A6sship%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B/959632847/)
【Vue.js】v-modelディレクティブとv-bindディレクティブの違い

##はじめに
この記事では、「v-modelディレクティブ」と「v-bindディレクティブ」の違いについて説明しています。

##v-modelディレクティブとは？
v-modelディレクティブは、フォーム要素の値とプロパティ間でデータバインディングを有効化するディレクティブです。つまり、フォーム要素が変更されるとその値がプロパティ要素に反映され、逆に、プロパティ要素が変更されるとその値がフォーム要素に反映されるようになります。

v-modelディレクティブは次のように使用します。
```<input v-model="プロパティ名">```

また、こうしたデータバインディングの機能を「双方向データバインディング」と呼びます。

##双方向データバインディング
双方向バインディングは、単方向のデータバインディングの組み合わせ（v-onディレクティブとv-bindディレクティブ）で成り立っています。このことを図を使ってわかりやすく説明したものが次のものです。

![スクリーンショット 2020-12-25 22.09.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/980442/979b607d-4e4f-b0ed-df5b-ce82c6922046.png)

##まとめ
v-modelディレクティブでは双方向（フォーム要素⇄プロパティ）で、v-bindディレクティブでは単方向（プロパティ→フォーム要素）で同期することができるといった違いがあります。

###参考文献
[関利晴（2020） 『21Stepで体得 Vue.jsハンズオン』](https://www.amazon.co.jp/21Step%E3%81%A7%E4%BD%93%E5%BE%97-Vue-js%E3%83%8F%E3%83%B3%E3%82%BA%E3%82%AA%E3%83%B3-StepUp-%E9%81%B8%E6%9B%B8-%E5%88%A9%E6%99%B4/dp/4865942297)
"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!"発生時の対処方法

#はじめに
前に別のハードウエアで使用していたものと同じホスト名、もしくはIPアドレスを持つ異なるホストにSSHで接続しようとすると、警告（WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!）が表示され、接続に失敗します。
RaspberryPi OSは、初期設定でホスト名として**raspberrypi**が設定されているため、同じPCを使用して２台以上のRaspberryPiの初期設定をしようとすると、２台目以降にホスト名でRaspberryPiにSSH接続する際、警告（WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!）が表示され、接続に失敗します。
本稿では、SSHで接続しようとした際に警告（WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!）が表示された場合の対処法について記載します。
#使用機材

<dl>
<dt>PC</dt>
<dd>MacBook Pro 16</dd>
<dd>2.3 GHz 8コアIntel Core i9</dd>
<dd>16 GB 2667 MHz DDR4</dd>
<dd>macOS Big Sur ver.11.1</dd>
<dt>RaspberryPi本体</dt>
<dd>RaspberryPi 4 Model B</dd>
<dd>8GB RAM </dd>
<dt>カメラモジュール</dt>
<dd>RaspberryPi Camera Module V2</dd>
<dt>AC充電器（RaspberryPi電源用）</dt>
<dd>ELECOM</dd>
<dd>MPA-ACUQ01WF</dd>
<dd>入力 AC 100-240V 50/60Hz 0.6A</dd>
<dd>出力 DC 3.6-6V 3A, 6-9V 2A, 9-12V 1.5A</dd>
<dt>microSDカード（OSインストール済み）</dt>
<dd>SanDisk</dd>
<dd>microSDXC UHS-I カード</dd>
<dd>32GB</dd>
</dl>

#対処方法
PCは過去にSSH接続したホストの名前もしくはIPアドレスと、その公開鍵の組み合わせを**known-hosts**というファイルに記録しています。SSH接続をする際に、ホストから送られる公開鍵を**known-hosts**内の情報と照合し、一致しない場合には、なりすましなどによる偽のホストへの接続を回避するために、警告を表示してSSH接続を停止します。
![スクリーンショット 2020-12-22 11.37.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679257/25ab168f-b79f-ced7-afa1-87131e9727b6.png)

この状態を解消するためには、PCが記憶している対象のホスト名と公開鍵の組み合わせの情報を削除します。
削除には、ssh-keygenコマンドを使用します。ターミナルから、`ssh-keygen -R  【ホスト名またはIPアドレス】`を入力します。ここで、【ホスト名またはIPアドレス】の部分は、SSHで接続しようとしたホスト名、またはIPアドレスであり、RaspberryPiの初期設定の際は、**raspberrypi.local**になります。
![スクリーンショット 2020-12-22 11.50.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679257/a8f986c2-dee0-67c5-294e-a31e3f31e264.png)
**known-hosts**から**raspberrypi.local**の情報を削除した後、再度SSH接続をすると、今度は警告は表示されず、ログインが可能になります。
![スクリーンショット 2020-12-22 11.52.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679257/6e8f73b9-d66d-7c13-0824-b13dabdabd96.png)


**known-hosts**は、ホームディレクトリの下の/ssh/の下にあります。
ssh-keygen実施前後のknown-hostsを比較すると、raspberrypi.localの情報が消去されていることがわかります。
####ssh-keygen実施前のknown-hosts
ホスト名 raspberrypi.loaclのエントリーが存在する。
![スクリーンショット 2020-12-22 11.47.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679257/5e4e9c4e-f07f-b1ad-47ff-a58c3a23a057.png)
####ssh-keygen実施後のknown-hosts
ホスト名 raspberrypi.loaclのエントリーは存在しない。
![スクリーンショット 2020-12-22 11.50.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679257/160e3ac5-d788-c9e4-c0ec-4f20887e071b.png)


#参考
今回の作業の実施にあたり、下記を参考にいたしました。

* [**SSH接続エラー回避方法：.ssh/known_hostsから特定のホストを削除する/削除しないで対処する3つの方法**](https://qiita.com/grgrjnjn/items/8ca33b64ea0406e12938)
* [**ssh-keygenコマンドの使い方**](https://qiita.com/hana_shin/items/2003698873a5782b7efd)
* [**【SSHをはじめて触る人へ】Linuxのホスト間認証とは？**](https://eng-entrance.com/linux-ssh-host)
一枚の長い画像を印刷したい、しかも折り返して。

この記事は、[Digital Identity Creative Div. Advent Calendar 2020 24日目](https://adventar.org/calendars/5030) の記事です。

こんにちは、DI制作チームの能勢です。

Webサイトのデザインや、LPのデザインをしていると、打ち合わせ用に印刷した時に「なんじゃこりゃあぁぁぁぁぁ！」というくらい細く印刷されたりしますよね？そんな時、やり方としてはいくつか方法があります。

## ペイントで切り貼りする

Windowsの往年の画像ソフトと言えば「ペイント」です。A4サイズのキャンバスを用意して、そこに切り貼りしていきます。最後に一枚の画像のとして保存すれば、意のままに印刷することができます。はい、時間かかりすぎますね、きっついです。

## パワーポイントに切り貼りする

MS Officeの代表格とも言えるパワポ。印刷するのに最適なツールです。ペイント同様に、1枚のスライドに切り貼りしていきましょう。ペイントよりも使い慣れたツールなので、時間短縮は間違いありません。できる人は人力でやってしまうのです。

## Print1 を使う

![2020-12-25-print1-001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237054/1a1c6dd4-b9bc-8cc0-aa0a-88f3899c66a2.jpeg)
[Print1](https://webnaut.jp/tools/print1/)

面倒な切り貼りともおさらばできる素晴らしいツール「Print1」のご紹介です！
サイトにアクセスし、縦長の画像をドラッグ＆ドロップしましょう！なんと、あら不思議！勝手に画像が折り返されて、いい感じにレイアウトしてくれるじゃありませんか！

社内の機密情報だから、外部のサービスでそんな事できない...。そんなあなたは、Print1 の右上にある「Fork me on GitHub」からforkしちゃいましょう！

画像の切り貼りのストレスから開放され、2021年が良い年になりますように！
Cuckoo Sandboxのanti-anti-sandbox処理（2）： 解析環境「らしさ」の払拭

Cuckoo Sandboxのanti-anti-sandbox処理（1）からの引き続きで，Cuckoo Sandboxのanti-anti-sandbox処理（2）です．

Cuckoo Sandboxでは仮想マシン上で動くゲストOSを解析環境として利用します．このゲストOSが仕事用や家庭用などの通常の用途の環境としてあまりに不自然だと，マルウェアは怪しいと気づくかもしれません．たとえば通常の環境では「最近使用したファイル」にファイルがたくさん並んでいて，ここに何もファイルがなかったら怪しいですよね．インストール直後ならいざ知らず，OSを数時間使うだけで，そこにファイルがたまっていくはずです．なお，「最近使用したファイル」は，エクスプローラーを起動すれば何も指示しなくてもウィンドウ内に表示されるので，そこで確認できます．

Cuckoo Sandboxは，おそらく解析環境であると見破られにくくするために，ゲストOSにダミーファイルを作成します．Cuckoo Sandboxのソースツリーのrecentfiles.pyというファイルに書かれたPythonプログラムに，その作成処理が記述されています．

Github: [master/cuckoo/data/analyzer/windows/modules/auxiliary/recentfiles.py](https://github.com/cuckoosandbox/cuckoo/blob/master/cuckoo/data/analyzer/windows/modules/auxiliary/recentfiles.py)

このプログラムを読めばわかりますが，ダミーファイルのファイル名は，10～20文字のランダム文字列に "txt", "rtf", "doc", "docx", "docm", "ppt", "pptx" のどれかの拡張子をランダムで付けたものです．ファイルのサイズと中身も，ランダムな長さのランダムなバイト列です．作る場所はデスクトップなどです．作るファイルの数は5～10です．

ファイル作成に合わせて，"File MRU" という文字列を含むパスのレジストリエントリも書き換えます．

Cuckoo SandboxのゲストOSの画面を見たことのある人は，解析の冒頭でデスクトップに，いかにもランダムなファイル名のWordファイルやテキストファイルがワッと作成されるのを見たことがあると思います．あれはマルウェアが作っているのではなく，Cuckoo Sandboxが作っています．

recentfiles.pyの主にこのへんのコードを読むと，上で述べたような，ダミーファイルを作成する方針がわかります．

```python:recentfiles.py
        for idx in xrange(random.randint(5, 10)):
            filename = random_string(10, random.randint(10, 20))
            ext = random.choice(self.extensions)
            filepath = os.path.join(dirpath, "%s.%s" % (filename, ext))
            open(filepath, "wb").write(os.urandom(random.randint(30, 999999)))

            SHELL32.SHAddToRecentDocs(SHARD_PATHA, filepath)

            set_regkey_full(
                "HKEY_CURRENT_USER\\Software\\Microsoft\\Office\\12.0\\"
                "Word\\File MRU\\Item %d" % (idx + 1),
                "REG_SZ", "[F00000000][T01D1C40000000000]*%s" % filepath,
            )
```

Cuckoo Sandboxは他の方法でも解析環境「らしさ」を払拭しています．たとえば同じディレクトリにあるhuman.pyというプログラムでは，自動的にマウスを動かしてくれたり，ウィンドウ内のボタンをクリックしてくれたりします．

Github: [master/cuckoo/data/analyzer/windows/modules/auxiliary/human.py](https://github.com/cuckoosandbox/cuckoo/blob/master/cuckoo/data/analyzer/windows/modules/auxiliary/human.py)

Cuckoo SandboxのゲストOSの画面を見たことのある人は，やはり，自分が何もしていないのにマウスがヒョコヒョコ動いてボタンもなぜかクリックされるのを見たことがあると思います．あれもCuckooのしわざです．

とはいえ，これらの処理も人間が見れば，明らかにランダムなファイルですし変なマウスの動きですので，怪しいことにはすぐに気づきます．どれくらい解析環境「らしさ」を払拭するのか，どの程度で妥協するのか，まさにいたちごっこの世界です．チューリングテストの話にも近くなってきますね．
Amazon SageMaker Pipelines を実際に使ってみた【後編】[re:Invent 2020]

# はじめに
**この記事は株式会社ナレッジコミュニケーションが運営する [Amazon AI by ナレコム Advent Calendar 2020](https://qiita.com/advent-calendar/2020/knowcom-amazonai) の 25日目にあたる記事になります。**

AWS が開催する [re:Invent 2020](https://aws.amazon.com/jp/about-aws/events/2020/reinvent/) で発表された Amazon SageMaker の新機能である Amazon SageMaker Pipelines についての記事です。[こちらの記事](https://qiita.com/knt078/items/73d4f94286238bd1077c)の続編となります。

# 定義したパイプラインの送信

[前回の記事](https://qiita.com/knt078/items/73d4f94286238bd1077c)の要領で定義したパイプラインを SageMaker Studio に表示したいと思います。前回記事のコードに続いて次のコードを実行します。

```python:
pipeline.upsert(role_arn=role)
```

# SageMaker Studio でパイプラインを監視する

まずは定義したパイプラインが Studio と連携できたか確認します。サイドバーの「Components and registries」から、プルダウンで「Pipeline」を選択すると、、

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/dff7318f-ad7e-15d2-e82f-6d05a0b3f58e.png)

定義した「AbalonePipeline」が表示されてます。こちらをクリックするとこのパイプラインが開きます。

![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/51724d8f-48fc-8261-e860-dc13385b0ffc.png)

こちらの「Executions」タブではパイプラインの実行履歴が表示されます。現時点ではまだ未実行のため何もない状態となっています。画面上の「Start an execution」をクリックすることでパイプラインの実行が開始されます。

隣の「Graph」タブでは定義したパイプラインがグラフィカルに表示されます。

![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/6b4cd746-1ad6-e511-b4b7-08e5530be0f1.png)

「Parameters」タブではパイプラインパラメータとして定義した値がそれぞれ表示されます。

![図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/d4f6f520-067c-d16e-f7d2-d1de69570f4b.png)

「Settings」タブでは Pipeline Arn や Role Arn の情報が記載されています。「Download pipeline definition file」でパイプライン定義を JSON 形式でダウンロードできます。

![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/e3c19667-cfc9-c4e5-ab79-1b5a38af57be.png)


## パイプラインの実行

「Executions」か「Graph」タブから「Start an execution」をクリックします。実行名などを記載して「Start」をクリックします。

![図6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/9aefdfb9-9ba0-df6d-96ac-c61a2d52c705.png)

「Executions」タブに遷移して実行成功のメッセージが表示されます。「View details」で実行内容の詳細が確認できます。

![図7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/61775ebe-c54c-df27-13e2-402bcde70178.png)

実行タブが開いて、現在実行中のプロセスが視覚的にわかるようになっています。「Parameters」「Settings」タブはパイプライン画面のタブと同じ内容となっています。

![図8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/2c1f40dc-1419-04de-2116-02fb8f488816.png)

パイプライン画面で実行経過が記録されます。複数実行時には他のパイプライン実行中の内容も表示されます。

![図9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/4e3d2ca2-34fd-2943-ac13-2007634fe3bb.png)

全てのステップの実行が完了しました！

![図10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/523d4449-a5b2-37dd-5b20-3a309e2e16d5.png)

パイプライン画面でもステータスが更新されています。

![図11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618791/eacee929-4dc3-098b-566c-ef84a5516994.png)


## パイプラインの削除

パイプラインの削除は SageMaker Studio の GUI 上ではなく生成した際にしたノートブックなどで実施します。次の１行で削除が完了します。（※前回の記事のノートブックの続きとして書いています。）

```python:
pipeline.delete()
```

# おわりに

繰り返し利用する機械学習パイプラインを GUI で管理できるようになり、 SageMaker Studio が一段と便利になったように感じます。 re:Invent 2020 では他にもたくさんのアップデートがあったのでキャッチアップしていきたいと思います！
Firebase/CloudFunctions監視(memory編)

# この記事について
[Firebase/CloudFunctions](https://firebase.google.com/docs/functions?hl=ja)の関数で使用してるメモリの監視についてになります。

割り当ててるメモリが足りない場合以下エラーがログで表示されます。
> Error: memory limit exceeded. Function invocation was interupted. 


メモリ不足エラーからメモリ不足に気づく前に
グラフからメモリ不足になる可能性があるのか？を知れる監視環境作成について書いてみました。

すでにメモリエラーが出ていて
手っ取り早くエラー解決を行いたい方は**メモリを上げる**から読んでいただけると:muscle:

対象者の想定は以下
・ Cloud Functionsで監視することって何？と気になる方
・ 正月休みで自社プロダクトで何かあった時のために知っておきたい方

# デプロイしてる関数全体のメモリ使用を知る

Customでダッシュボードを作ります。

GCPコンソールを表示
1.ナビゲーションメニュ-
2.Monitoring
3.ダッシュボード
4. "+ CREATE DASHBOARD"を選択
5. お好きなグラフライブラリを選択(Lineが良さそうかと
![スクリーンショット 2020-12-25 18.06.49（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/1d871e65-b898-7556-5c24-d8999d4ef038.png)


## グラフの設定
Chart Title: お好きな名前を入力してください
Resource type: Cloud Function
Metric: Memory usage
percentile: 5, 50, 95, 99
5でメモリ使用量が高いのであれば慢性的にメモリ不足
99でメモリ使用量が高いのであれば何かがきっかけで跳ねてる可能性有
![スクリーンショット 2020-12-25 18.05.43（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/4ad9fa68-e582-a971-66f5-cda6cee88f2d.png)

## 関数指定でグラフ作成
Filterでグラフ化したい関数名を選びます。

Label: function_name
Value: グラフ化したい関数名

![スクリーンショット 2020-12-25 17.38.48（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75593/e260a503-589a-fe41-dcb3-a8b9199a133e.png)

実は関数指定の場合は
特にCustomでグラフを作る必要はなく以下手順でモニタリングすることが可能です。

GCPコンソールを表示
1.ナビゲーションメニュ-
2.CloudFunctions
3.詳細を知りたい関数名をクリック

知れるもの
・呼び出し回数/秒
・実行時間(ミリ秒/呼び出し)
・メモリ使用率(MB/呼び出し)
・アクティブなインスタンス

↑Customで作成しなくても
関数に関するモニタリング対象がたくさん入ってるバラエティパックみたいな感じw

# Customグラフよりメモリが足りてない関数を知る
見やすさの観点より
作成したグラフを全画面表示に変更して確認することをお勧めします。

全画面にすることで以下情報が追加で閲覧できるためです:relaxed:
・function_name
・region
・trigger_type
・memory(現状設定してるメモリサイズ
・value(最新状況で使われているメモリサイズ

着目点
・グラフ
設定してるmemoryを超えてる、もしくは超えそう(右肩上がり)
・memory vs value
設定してるmemoryとvalueが近い値もしくはvalueがmemoryを超えている

※設定してるパーセンタイルにもよると思います

# メモリを上げる
前提
メモリが足りてない関数を確認後、
メモリ容量をいくつに設定するのが妥当か？をグラフから観察してメモリを上げるためです。
無闇にMax容量に増やすとかそういうのは節約でやらないようにするためです。

## GCPコンソールから
GCPコンソールを表示
1.ナビゲーションメニュ-
2.CloudFunctions
3.割り当ててるメモリを変更したい関数名をクリック
4.編集をクリック
5. 変数、ネットワーク、詳細設定をクリック
6. 割り当てられているメモリからメモリを変更して次へをクリック
7. デプロイをクリック
8. デプロイ完了後は変更したメモリが割り当てられているか確認
メモリの割り当て確認はGCP or FirebaseConsoleどちらからでも可能です。
##コードから
[タイムアウトとメモリ割り当てを設定する](https://firebase.google.com/docs/functions/manage-functions?hl=ja)

```javascript
const runtimeOpts = {
  timeoutSeconds: 300,
  memory: '1GB'
}

exports.myStorageFunction = functions
  .runWith(runtimeOpts)
  .storage
  .object()
  .onFinalize((object) = > {
    // do some complicated things that take a lot of memory and time
  });
```

#さいごに

まだ確証はないですが負荷が割とあるサービスを運用してる場合
GCPコンソールから関数ごとのログを見ようとすると表示まで時間がかかる感じがあります。

ログをみたい関数が複数ある場合一つずつ確認しようとすると表示までの時間が長いストレスと焦り(緊急性が高い場合)があると思ってます。
そのためデプロイしてる関数は全てまとめて一つのページで見れるような環境にしておくと便利かなと！
カレンダー日付選択をでっかくする(Ripjar/Material-datetime-picker)

### デモ

https://codepen.io/qwe001/pen/XWjzjoa

単にCSSでレイアウト上書きしただけですが、誰かのお役に立てれば…

追記: 時計部分はでっかくしてないです。私はカレンダー部分しか必要ないので。

### CSS

```css
.c-datepicker {
  width: 600px;
}

.c-datepicker__header-day {
  font-size: 18px;
}

.c-datepicker__day-head {
  font-size: 20px;
  height: 42px;
}

.c-datepicker__day-body {
  font-size: 18px;
  width: 76px;
  height: 36px;
}

.u-hover-ball-effect:hover:before, .c-datepicker__day-body:hover:before, .c-datepicker__clock__num:hover:before, .c-datepicker__clock__am-pm-toggle label:hover:before {
  width: 32px;
  height: 32px;
}

.c-datepicker__day--selected::after {
  width: 32px;
  height: 32px;
}

.rd-month-label {
  font-size: 20px;
}
```
最高のコントリビュート先の見つけ方、そしてコントリビュート をするまで【OSSに初めてコントリビュートしたい方へ】

# 最高のコントリビュート先の見つけ方
### コントリビュートできるOSSをGithubのTopicsから探す
https://github.com/topics

### 翻訳の案件を探す
https://www.transifex.com/

### OSSの初心者向けのissueを見つける
https://github.com/ユーザー名/レポジトリ名/contributeで調べると、
このレポジトリでの、初心者向けのissue（good first issue）を見つけることができます！

下はchart.jsというライブラリのgood first issueの例です
https://github.com/chartjs/Chart.js/contribute

# コントリビュートの仕方
ここでは、githubのdocsのレポジトリに対してコントリビュートしたい場合を想定します。
https://github.com/github/docs

### forkする
![スクリーンショット 2020-12-25 17.47.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/c8296f12-dffd-772a-2c44-3885a978858d.png)

これを行うと、自分のレポジトリにfork元のレポジトリが作成されます！
![スクリーンショット 2020-12-25 17.47.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/e3be12d8-80dd-ef16-cda9-8d707d63d4a8.png)


### 自分のレポジトリ一覧に入った、コントリビュート先のレポジトリをcloneしよう
![スクリーンショット 2020-12-25 17.48.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/95294dbd-3ba4-949d-696a-f98468598f07.png)


git clone あなたのレポジトリのURL

### remoteのrepoを登録しよう
ターミナルでupstreamをrepoに追加しよう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/9c1c89a7-231f-3aaa-43c0-28ab223677c9.png)


```
git remote add upstream リモートのrepoのhttpsのURL
```

ちゃんとupstreamを登録できてるか確認
```
git remote -v
```
こんな感じで、originとupstreamが確認できてばOK!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/dd6bccfd-0af5-c174-5606-9c00709ba5b1.png)


### 最新のremoteのコミットをローカルに落としてくるには
git pull upstream main

### 環境構築して、ローカルで立ち上げてみよう！
github/docsだったら、develoment.mdに環境構築の方法を書いてくださっております。
こんな風に環境構築の方法を書いてくれていると思うので、みなさんがコントリビュートしたいレポジトリをよくよくご確認ください！
https://github.com/github/docs/blob/main/contributing/development.md
### じゃあ変更していこう！
の前に、作業用のブランチを切っておこう！
ブランチ名は、あなたが変更したい作業名を英語でつけてみよう！

```
git checkout -b button-stylefixing upstream/main
```
これはupsteam、つまりremoteの最新のmainブランチの状態からブランチを切ってます！

**じゃあ、変更していこう！**
いい感じに変更できたら、

```
git add .
git commit
git push origin button-stylefixing(さっき切ったブランチ名)
```

## Github上でContribute先のrepoのPR作成ページにて、自分のForkブランチの先ほど変更をしたブランチとContribute先のmainブランチとの差分を確認し、PRを作成！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411851/ff2e562a-8198-75bd-3aa9-ba2b075677ce.png)

こんな風にPRは作れるよ！！
お互い頑張ってOSSに貢献しようね！
[メモ] Jestで特定のメソッドが呼ばれていないことをアサートする

# やりたいこと

テストコードをJestで書いていて、特定のメソッドがコールされてないことをチェックしたい
というかES6以下で対象メソッドが動くことを確認したい

もっと良いやり方ありそうなのでご存じの方教えて頂きたいです...

# やりかた

下記のような指定の文字で終わっているか確認するメソッドがあるとします。
`endsWith`を使ってはいけない制約があるとします

```js:app.js
// 指定の文字で終わっているか確認します
// ES6で導入されたendsWithは使ってはいけない

/**
 * @param {String} str
 * @param {String} target
 */
function confirmEnding(str, target) {
  const len = str.length;
  return str.substring(len - target.length, len) === target;
}

module.exports = confirmEnding;
```

`endsWith`が使われていないことをアサートしたい場合
メソッド自体を文字列化し、対象のメソッドが文字列に含まれていないことを確認します

```js:app.test.js
const confirmEnding = require("./ConfirmtheEndingTargetStr");

test("confirmEnding()はendsWithを使用していけません", () => {
  const result = confirmEnding
    .toString()
    .replace(confirmEnding.name, ""); // メソッド名に`endsWith`が入ってるとだめなので、消しておく
  expect(result.includes("endsWith")).toBeFalsy();
});
```
PandasのDataFrameを3Dプロットする

研究でPandasのDataFrameを３次元プロットしたくなりました。
例えば、次のようなデータがあったとしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255633/42bc60d2-519a-1421-e0f2-942a92d22fc5.png)
このデータをx軸はA、y軸はB、z軸はCの値でプロットするのは非常に簡単でいっぱい記事があり、例えば以下のようなコードで可能です。

```python
import pandas as pd
%matplotlib notebook
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

df = pd.DataFrame({'A':[0,1,2,3,4],
                   'B':[0,1,4,9,16],
                   'C':[0,1,8,27,64]})
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
sc = ax.scatter(df.A, df.B, df.C, s=100)
```
![ダウンロード (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255633/fc414368-1671-b138-c053-10eb420dfb08.png)
この画像のようにプロットできるでしょう。

さて本題ですが、僕はこのデータをcolumnをx軸に、indexをy軸に、そして各データをz軸に、合計15点の3Dプロットを行いたくなりました。
結論以下のコードでできるのですが少し面倒でした。

```python
import pandas as pd
%matplotlib notebook
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

df = pd.DataFrame({'A':[0,1,2,3,4],
                   'B':[0,1,4,9,16],
                   'C':[0,1,8,27,64]})

#文字だとプロットできないのでcolumn名を数字に変更
df.rename(columns={'A':1, 'B':2, 'C':3}, inplace=True)

X = np.array([])
Y = np.array([])
Z = np.array([])

for i in range(df.index.size):
    X = np.concatenate([X, np.full(df.columns.size, df.index[i])], 0)

for i in range(df.index.size):
    Y = np.concatenate([Y, np.array(df.columns)], 0)

for i in range(df.index.size):
    Z = np.concatenate([Z, np.array(df[i:i+1])[0]], 0)
    
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
sc = ax.scatter(X, Y, Z)
```
以下の画像のようにできました。
![ダウンロード (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255633/613807b8-1f0e-ab4a-124d-047a4c68bc84.png)
お疲れ様でした。
誰かの参考になったらうれしいです。
[php]usort関数について

#usort関数について
自身で勉強していて、わかりにくかった部分であるusortに関して、まとめてみました。

``公式php.net``から

```
usort ( array &$array , callable $callback )
```
と説明されています。

* **&$array** :入力する配列
* **$callback**: コールバック関数

要するに、第1引数で並び変えたい配列を渡し、第２引数で並び替える順番を定義した比較関数を入れるということです。
#使い方

* 今回使う配列

```
$data = [　
  ['name' => 'taguchi', 'score' => 80],
  ['name' => 'kikuchi', 'score' => 60],
  ['name' => 'hayashi', 'score' => 70],
  ['name' => 'tamachi', 'score' => 60],
];
```

* 自らが定義した比較関数

```
  function scores($a, $b) {
    if($a['score'] === $b['score']) {
      return 0;
    }
    return $a['score'] > $b['score'] ? 1 : -1;
  }
```
**比較関数で行っていること**

* 比べるscoreを`$a, $b`に置き換える

* scoreが同じ場合、0を返す

* `$a`が`$b`より大きい場合、1を小さい場合-1を返す。

よって0の場合は順番に影響はない1の場合、繰り上げ-1の場合、繰り下げられる。

* usort関数による呼び出しとprint_rによる表示

```
usort($data, 'scores');　//並び替えた関数の表示

print_r($data);
```

* 並び替える前の配列の結果表示

```
Array
            [name] => taguchi
            [score] => 80

            [name] => kikuchi
            [score] => 60

            [name] => hayashi
            [score] => 70

            [name] => tamachi
            [score] => 60
```

* 並び替えた後の配列の結果表示

```
Array
            [name] => kikuchi
            [score] => 60

            [name] => tamachi
            [score] => 60

            [name] => hayashi
            [score] => 70

            [name] => taguchi
            [score] => 80
```
#終わりに
usort関数とは、配列の中身を並び替えるというものだとわかりました。使い方は、()の中に並び替えたい配列とどのような順番かを定義した関数を入れるという簡単なものでした。