OmniAICreator/Qiita-1.07M huggingface dataset:
画像遅延読み込み(loading属性 + IntersectionObserver)

# はじめに
担当しているサイトがIE非対応になったため、それとともにパフォーマンス改善処理を見直し画像遅延読み込みが改善の余地があったため今回loading属性+InterSectionObeserverで実装した。

## loading属性
> Chrome 75からサポート。
loading属性とはブラウザがネイティブで遅延ロードをサポートするもの（参照: https://coliss.com/articles/build-websites/operation/work/native-image-lazy-loading.html）

## IntersectionObserver
> Intersection Observer APIは直訳すると交差監視APIという名前です。これは要素と要素の交差を検知するAPIになります。(参照: https://ics.media/entry/190902/)

## 実装
本来、loading属性はHTMLのみで完結しますが、対応ブラウザは`Chrome 76`、`Firefox 75`、`Chromium 版 Edge` においては、デフォルトで利用可能になっていますがそれ以外が使用不可なので使用できない箇所はJavascriptで実装していきます。

### HTML

```html
<img class="hoge" src="/images/common/noimage.png" data-src="/images/hoge.png" alt="hoge" loading="lazy">
```
`src="/images/common/noimage.png"`: 遅延読み込み前に読み込む1×1画像
`data-src="/images/hoge.png"`:読み込む画像（今回はJavaScriptでも使用する可能性があるため、data-srcで指定）
`loading="lazy"`: loading属性セット

### JavaScript

```javascript
const lazyLoad = function () {
  const dataSrc = document.querySelectorAll("img[data-src]")
  const sources = document.querySelectorAll("source[data-srcset]");
  //対応ブラウザかチェック
  if ("loading" in HTMLImageElement.prototype) {
  //loading属性対応の場合 data-srcの値をsrcへセット
    if (sources.length !== 0) {
      sources.forEach(function (source) {
        source.srcset = source.dataset.srcset;
      });
    }
    dataSrc.forEach(function (img) {
      img.src = img.dataset.src;
    });
  } else {
    //loading属性非対応の場合　IntersectionObserverを行う。
    const targets = Array.from(dataSrc);
    const img_path = "data-src";
    const options = {
      // 上下100px手前で発火
      rootMargin: "100px 0px"
    };

    // 初期化
    const observer = new IntersectionObserver(callback, options);

    targets.forEach(function (img) {
      // 監視の開始
      observer.observe(img);
    });

    // コールバック
    function callback(entries, object) {
      entries.forEach(function (entry) {
        // 交差していない
        if (!entry.isIntersecting) return;

        // ターゲット要素
        const img = entry.target;

        // 遅延ロード実行
        loading(img);

        // 監視の解除
        object.unobserve(img);
      });
    }

    // 遅延ロード
    const loading = (img) => {
      // data-srcの値を取得
      if (sources.length !== 0) {
        sources.forEach(function (source) {
          source.srcset = source.dataset.srcset;
        });
      }
      const src_val = img.getAttribute(img_path);
      if (src_val) {
        // 画像パスを設定
        img.src = src_val;
        img.onload = function () {
          // data-src属性を削除
          this.removeAttribute(img_path);
        };
      }
    };
  }
}

const _lazyload = lazyLoad();

export default _lazyload;


```

"loading" in HTMLImageElement.prototypeで対応ブラウザだとtrueが返される。
IntersctionObserverの処理は[別記事](https://qiita.com/nakamo-03/items/120182cede209d594100)で紹介しているので割愛。

## おわり
loading属性での遅延読み込みがとにかく記述が少なく、HTMLで完結してくれるから楽。
対応ブラウザが今後も増えてくれることを願います。

## 参照記事
[ics.media JSでのスクロール連動エフェクトには](https://ics.media/entry/190902/)
[Chromeに実装される新機能『loading属性』について解説、ついにブラウザがネイティブで遅延ロードをサポート](https://coliss.com/articles/build-websites/operation/work/native-image-lazy-loading.html)
[Ruby][macOS] 任意の文字列をクリップボードにコピーする

## 方法

[IO.popen](https://docs.ruby-lang.org/ja/latest/method/IO/s/popen.html) を使用する。

```rb
def pbcopy(str)
  IO.popen('pbcopy', 'w') { _1.print(str.to_s) }
end
```

![2021-02-22 17-47-26.2021-02-22 17_48_21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6697/a1d09ad4-9cc5-33fc-20e7-27d006c4c342.gif)
swift ビルド押して黒い画面... なーぜーー

初めて黒い画面になった時はあせりました。

再起動でなおるだろうと思ったがそれでもなおらない...

そんな時！

ViewControllerの中の
is initial ViewController
に注目して欲しい！
チェックマークが外れてる！
![スクリーンショット 2021-02-22 17.39.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1099804/e8f88f62-32bb-00b6-e569-6d5cc4f09d51.png)



ここにチェックマークをつけるとなおります。
![スクリーンショット 2021-02-22 17.42.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1099804/2fa60410-5ac7-cfd5-4751-5a7104acc14f.png)

つまり表示する最初の画面がわからないので
最初の画面はこれやでー
って言うマークを付けてあげた状態です。


ちなみにこれをつけると
storyboardの最初に決めた画面に右矢印マークでます。
![スクリーンショット 2021-02-22 17.46.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1099804/ef4a405a-cefb-2aee-cef4-50ddb79120a5.png)


xcode君が最初の画面がどれか分からなかったから
しょぼくれて闇落ちしてた感じです。
【ruby】CSVファイル読み込み時に1列目のデータが読み込めない時の対応

初期投入用のDB登録処理を作成した際、CSVファイルを読み込んでデータを登録するようにしたのですが、1列目に登録していたデータを読み込めない現象が発生しました。
対応方法を調査した時によく見かけたBOMの有無の確認方法が、CSVだとうまく確認できないようなので、備忘として残します。

# 環境
macOS Big Sur 11.0.1
ruby 2.7.2

# 現象
以下のようなCSVファイルを、

```csv:food.csv
id,category,name
1,北京料理,北京ダック
2,北京料理,杏仁豆腐
3,北京料理,炸醤麺
```

以下の処理で読み込ませようとしたとき、1列目である`id`列が読み込めず、nilとなってしまう。
※データ取得時にヘッダ名を指定したときのみ。indexで指定すると問題なく登録できる。

```rb:
require 'csv'

CSV.foreach("food.csv", encoding: 'UTF-8', headers: :first_row) do |row|
  Food.seed(:id) do |s|
    s.id = row['id']             # 登録されたデータが全てnilになる。
    # s.id = row[0]              # ヘッダ名ではなく、indexで指定すると、問題なく登録できる。

    s.name = row['name']         # 問題なし。
    s.category = row['category'] # 問題なし。
  end
end
```

※仮にID列を削除した場合、今度は1列目となる`category`列が読み込めなくなる。

```csv:food.csv
category,name
北京料理,北京ダック
北京料理,杏仁豆腐
北京料理,炸醤麺
```

# 原因
使用したCSVファイルがUTF-8であり、BOM付きであったため。
BOMが文字として扱われているようで、正常にCSVが読み込めない結果となってしまった。

# BOM付きファイルの確認方法

## 確認方法1
ターミナルより、`file`コマンドで確認できる。
ただ、CSVファイルではBOM付きであることを判別できないようなので、確認方法2で確認しています。

```shell:txtの場合
$ file sample.txt
sample.txt: ASCII text    # BOMなしの場合
$ file sample_bom.txt
sample_bom.txt: UTF-8 Unicode (with BOM) text    # BOM付きの場合
```

```shell:csvの場合
$ file food.csv
food.csv: CSV text    # BOM付きの場合（BOMなしでも同じ表示となる）
```

## 確認方法2
以下のコマンドをターミナルで実行し、表示されたファイルがBOM付きファイル。
※サブディレクトリ配下のファイルを含め表示されます。

```shell:
$ find . -name '*.csv' | xargs grep -l ^$'\xef\xbb\xbf'
./food.csv    # BOM付きファイルが表示される。
```

# 対応方法
`nkf`コマンドを使用する。macには標準で入っていないため、インストールする必要あり。

```shell:
$ brew install nkf

$ nkf --version    # インストールの確認
Network Kanji Filter Version 2.1.5 (2018-12-15) 
Copyright (C) 1987, FUJITSU LTD. (I.Ichikawa).
Copyright (C) 1996-2018, The nkf Project.
```

以下のコマンドより、BOMなしのファイルへ変換できる。

```shell:
$ nkf --overwrite --oc=UTF-8 food.csv
```

これで、1列目の`row['id']`を問題なく取得できるようになりました。

#参考記事
**・[UTF-8 BOM を確認・追加・削除するコマンド [Linux, macOS 編] | CodeNote](https://codenote.net/linux/4431.html)**
**・[Mac(bash)でBOM付きファイルを抽出しBOMを削除するコマンド | KEYPOINT – キー・ポイント株式会社 開発日誌](https://www.key-p.com/blog/staff/archives/96130)**
**・[UTF-8のBOMの確認、追加、削除 – おもむくがままに](https://blog.k-sakabe.com/2020/03/17/utf-8%E3%81%AEbom%E3%81%AE%E7%A2%BA%E8%AA%8D%E3%80%81%E8%BF%BD%E5%8A%A0%E3%80%81%E5%89%8A%E9%99%A4/)**
プロトコルとは

##プロトコルとは
コンピュータ同士が通信する際に`守らなくてはいけない決まり事`のことです。
プロトコルに従うことで、異なるコンピュータや端末との通信が可能になります。
メールの送受信、データのダウンロード等、目的によって使われるプロトコルは異なります。

##プロトコル名と使用事例
###1.インターネット
`HTTP（HyperText Transfer Protocol）`：Webページを表示する時。
`HTTPS（HyperText Transfer Protocol over SSL/TLS）`：暗号化されたWebページを表示する時。
→HTTP通信が、SSLによって暗号化されたプロトコル。
`SSL/TLS`：インターネットなどのコンピュータネットワークにおいてセキュリティを要求される通信を行う時。

###2.電子メール
`SMTP（Simple Mail Transfer Protocol）`：メールの送信する時。
`POP(Post Office Protocol)`：メールを受信する時。
`IMAP（Internet Message Access Protocol）`：POPと同じでメールを受信する時。
→①POPは受信したメールをPCにダウンロードして管理する。
 ②IMAPはサーバーに置いたままで管理する。
`MIME（Multipurpose Internet Mail Extensions）`：メールに画像やファイルを添付する時。
`S/MIME（Secure/Multipurpose Internet Mail Extensions）`:メールを暗号化する時。

###3.ファイルのダウンロード、アップロード
`FTP(File Transfer Protocol)`：データの転送をする時。

###4.時刻を同期
`NTP(Network Time Protocol)`：アクセスログ(通信の記録)の解析を正確に行うために、Webサーバやデータベースサーバなどのサーバ間で時刻を一致させる時。

###5.データ転送
`TCP/IP（Transmission Control Protocol/Internet Protocol）`：インターネット通信やメールの送受信などを支えるために使う。
`TCP`：データをもれなく転送するためのプロトコル。
`IP`：目的の相手にデータを転送するためのプロトコル。
　　　IPには、v4(バージョン4)とv6(バージョン6)があり、v6では通信の暗号化機能が追加された。
【Googleスプレッドシート】英文の横に未学習単語を表示する

#はじめに
英語の長文の横に未学習単語を表示できれば、英語学習がより効率的になると思い作ってみました。

※ダウンロードやコピーは可能ですが、悪用はしないでください。
https://docs.google.com/spreadsheets/d/1acLSBncWnPWqKx2UC9pVsQo3qkYFISckKbvNsa7BrFQ/edit#gid=1631947654

#使い方(Voa As it is シート)
![未学習単語と長文 - Google スプレッドシート - docs.google.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120085/333fa0d7-a3c5-b58e-f56c-504c4af63faf.png)
事前準備(1~2)、その他機能(3~6)

1.  コピー・ダウンロードした後、既知単語シートにある単語から知らない単語を除いてください。
　また、すでに知っている単語があれば適宜追加してください。
　※記号は消さないでください。
2.  P列に英単語が表示された後、知っている単語をコピー、Ctrl+Shift+Vで既知単語シートA列に追加してください。
3.  VOA As it isシートA列の下のほうでは、翻訳を見ることができます。
4.  N1をクリックすると、記事ページに飛ぶことができます。
5.  O列をクリックすると、英辞郎の該当単語のページに飛びます。
6.  Q列の『ロングマンで検索』をクリックして頂くと、ロングマンの訳に飛びます。

#他のシートについて
・ニュースSheetでは、[News in Levels](https://www.newsinlevels.com/)という、一つのニュースを三段階のレベルで読むことができるサイトをスクレイピングしています。

・VOA Health Sheetでは、画面の半分が英文、もう片方が日本語訳になっています。

・スクレイピング・シート6 Sheetでは、アルクの英辞郎からスクレイピングしています。

#注意事項
・スクレイピングのやりすぎはサーバーに負荷をかけてしまいます。
・著作権の問題等もありますので、実行する前にサイトの利用規約等を確認してください。
・読み込みが遅いと感じた場合、不要なシートやセルを削除して下さい。

#追記
2021/03/18
・**今まで対応していなかった一部の記号に対応致しました。申し訳ありませんが、以前からお使いの方はVOA As it isシート J1 を差し替えて下さい。**
・既知単語シートの単語が5000語を超えました。一部[こちら](https://eigo-duke.com/tango/tangoindex.html "英単語の一覧")のサイトを参考にしました。
・VOA As it isシートA1は =A82 となっています。好きな英文を A1 に入力すると、その英文に含まれる未習単語一覧を表示できます。また、改行が多い文章については A81 に入力し、A1は =A83 と入力することで A1 に改行なしの文章が表示されます。
jQueryとは？

# jQueryとは


`jQuery`とは、
>
- JavaScriptでできることを、より簡単な記法で実現できるように設計されたJavaScriptライブラリの一種
- JavaScriptライブラリのデファクトスタンダード

<br>

>
`ライブラリ`とは
>>
- 汎用性の高い複数のプログラムを再利用可能な形でひとまとまりにしたもの
- 体ではプログラムとして動作させることはできない
- 他のプログラムに何らかの機能を提供するコードの集まり

## メリット

マウスオーバーすると色が変わる機能のコードを例として比較

```js:jQueryを使用
$(function () {
  $(document).on("mouseover", "h1", function () {
    $(this).css({ "color": "blue" });
  }).on("mouseout", "h1", function () {
    $(this).css({ "color": "" });
  })
});
```

```js:JavaScriptだけ
window.onload = function () {
  document.querySelector("h1").addEventListener("mouseover", function (event) {
    this.style.color = "blue"
  }, false);
  document.querySelector("h1").addEventListener("mouseout", function (event) {
    this.style.color = ""
  }, false);
};
```

- 短いコードで簡単に実装する事が可能
- 長くなる記述を簡易的な記述にできる
Tensorflow2でmodel.predictの推論が遅い!ので他の方法を検証した

#はじめに

動画に対してリアルタイム解析を行う場合、どうしても推論速度を意識しないとなりません。30fpsで解析するためには前処理・後処理を合わせて33msec以内に行う必要があります。
最近tensorflow2に移行したのですが、どうも`tf.keras.model.predict`が遅いような気がして、ほかに早く推論できる方法はないかと調べてみました。

すでに公知の事実のようでしたが、結果として1枚の画像を解析する場合に`tf.keras.model.predict`は悪手のようです。

[参考サイト](https://github.com/tensorflow/tensorflow/issues/33340)

検証条件
CPU：Ryzen9 5950x
GPU: RTX3070
Memory: 64GB
OS: Windows10 Pro (jupyter notebook使用)
Python: 3.7.9
tensorflow: 2.4.0
CUDA: 11.0
cuDNN: 8.0.5

# 検証の方法

今回は`tf.keras.applications.VGG16`を特に変更せず使用しました。
速度を検証する手法は以下の３種類です。
１．`model.predict()`
２．`model()`
３．`model.predict_on_batch()`

検証用のデータは下記の２種類です。
A. １枚の画像を想定した`np.random.rand(1,224,224,3)`で生成したデータ
B. 複数の画像（４枚）を想定した`np.random.rand(4,224,224,3)`で生成したデータ

```python3:python3
import tensorflow as tf
physical_devices = tf.config.list_physical_devices('GPU')
if len(physical_devices) > 0:
    for device in physical_devices:
        tf.config.experimental.set_virtual_device_configuration(device, [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=3072)])
else:
    print("Not enough GPU hardware devices available")

import numpy as np
import timeit

model = tf.keras.applications.VGG16()
arr = np.random.rand(1,224,224,3)
batch = np.random.rand(4,224,224,3)
#注：以下はjupyter notebookでの表記です。
%timeit  -n 50 model.predict(arr)
%timeit  -n 50 model(arr,training=False)
%timeit  -n 50 model.predict_on_batch(arr)

%timeit  -n 50 model.predict(batch)
%timeit  -n 50 model(batch,training=False)
%timeit  -n 50 model.predict_on_batch(batch)
```

#結果

|                          | データAに対する推論速度 | データBに対する推論速度 | 
| ------------------------ | ----------------------- | ----------------------- | 
| `model.predict()`          | 21.8 ms ± 216 µs        | 28.2 ms ± 267 µs        | 
| `model()`               | 6.33 ms ± 53.9 µs       | 14.9 ms ± 27.3 µs       | 
| `model.predict_on_batch()` | 7.24 ms ± 49.5 µs       | 14 ms ± 140 µs          | 

上記結果をみて驚いたのですが、結構差がありました（ご存じの方にはすみません・・・）

ということで１枚の画像に対しては
`model()` < `model.predict_on_batch()` < `model.predict()`の順であり

複数の画像に対しては
`model.predict_on_batch()` < `model()` <  `model.predict()`の順でした。

いずれの場合でも`model.predict()`は最も遅く、 `model()`、`model.predict_on_batch()`がほぼ同程度の速度でした。

#考察

そもそもtensorflow2の`model.predict()`は１つのデータに対して実行するのに適していないとのことです[(ソース)]
(https://github.com/tensorflow/tensorflow/blob/v2.4.1/tensorflow/python/keras/engine/training.py#L1502-L1647)。

具体的には、1つのデータに対しては`model(x)`もしくは`model(x, training=False)`が適しているということでした。
また`model.predict_on_batch()`も同様に早いということです[(ソース)](https://github.com/tensorflow/tensorflow/issues/33340)。

ということで`model()`もしくは`model.predict_on_batch()`で推論する方が良いようです。
複数枚画像の処理においては`model.predict_on_batch()`の方が高速ですので、
常に`model.predict_on_batch()`でよいような気がします。。。

#おわりに

今更ながらmodel.predict()が遅くいことを知りました。。。
そのあたりAIの実装においては結構重要だと思うのですが、公式のチュートリアルに書いてあったかなぁ・・・？
Nebular Auth firebase(@nebular/firebase-auth)でthis.afAuth.signInWithPopup is not a functionが出たときの解決

情報が無かったのでメモ...
#エラー

```
core.js:6157 ERROR TypeError: this.afAuth.signInWithPopup is not a function
```

#解決
8.0.0以上のfirebaseが必要

```package.json
 "firebase": "^8.0.0"
```
再インストール

```
npm uninstall @angular/fire    
npm install @angular/fire@latest
```
Laravelでall()の後にpaginateが使えなかった

#やりたかったこと
newsというテーブルからpaginateを使って10個ずつデータを表示したかった

#やったこと
```$posts = News::all()->sortBy('updated_at')->paginate(10);```
newsを更新順に並べ替え手からpaginateしようとした。
#結果
```Method Illuminate\Database\Eloquent\Collection::paginate does not exist.```
、、怒られた。
#原因
こちらの記事：[Laravel Paginatorについて](https://qiita.com/mingliangli/items/ef6b80ec5a2511e138e5)
に近いことが書いてあった。

ざっくり言うと、all()はcollectionを返し、collectionにpaginateなんてメソッドはない、ということだと思う。


#解決方法
```$posts = News::orderBy('updated_at', 'desc')->paginate(10);```
にしたらうまくいった。

ここでも、sortByがcollectionメソッドだから使えなかったのでorderByを使った。
名前似てるのにややこしい。
FastAPIでカレンダーを作ってみた

#はじめに

ノリでFastAPIを利用してカレンダーを作ってみたのでメモ。

PythonのFastAPIとは
https://fastapi.tiangolo.com/ja/

#バージョン
Python： 3.9.0
uvicorn：0.13.3

#サンプルコード
https://github.com/toshi-imai/python_FastAPI_Calender

#機能

- 1年間のカレンダーが表示される。
![calender01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111664/9c31cbc8-f1fb-9ceb-878f-49f055c2f220.png)

- 日付をクリックすると日付と戻るボタンだけのページに遷移
    - 適当に編集して利用可能

![calender02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1111664/8ffe4d43-edec-63e8-0080-dc2e1a63a318.png)
Herokuにデプロイした時のエラーについての備忘録

**Herokuにデプロイした際に出たエラーとその解決策について復習も兼ねて解説したいと思います。**

まず`git push heroku master`をした後に`heroku run rails db:migrate`をしてサイトにアクセスしたら以下のようなよく見るエラー文が表示されました。
<a href="https://gyazo.com/b784ffbb6ea85377e9f91ed623e5dded"><img src="https://i.gyazo.com/b784ffbb6ea85377e9f91ed623e5dded.png" alt="Image from Gyazo" width="709"/></a>
そこで`heroku logs --tail --app 【アプリケーション名】`でログを確認してみました。

```
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR:  relation "posts" does not exist
```
確認した所、上記のエラーメッセージが見つかりましたので恐らくここが原因を示していると判断しました。

そこで調べてみたところどうやらHerokuではローカルではmigrationが普通に出来ていたとしても、Heroku上では**migration fileは日付順にmigrateされる**とのことでした。

ここでローカルのデータベースを確認してみます。
`rails db:migrate:status`を実行

```
 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20210118033907  Create posts
   up     20210118044855  Devise create users
```

上記のようになっていたのでマイグレーションを実行すると、先にpostsテーブルが作られるがリレーションをしているusersテーブルがこの時点では作成されていないためエラーが出ていたみたいでした。
したがってusersテーブルを先に作られるようにする必要があります。
**※注釈）リレーションをしているとは「userとpostモデルで1対多などを指定しているの意味」**

なので、`rails db:drop`をした後にマイグレーションファイルのdevise_create_usersの日付（= Migration ID：上記の画像の箇所）をpostsより早い日付になるように手動で修正します。
<font color="blue">=>「ローカルのマイグレーションファイル名の日付箇所を修正する」</font>
あとは`rails db:create`して`rails db:migrate`します。

<font color="red">**※注意）rails db:dropしたらデータベースの中身は消えますので注意してください。**</font color>

修正が完了したらもう一度ローカルのデータベースを確認します。
`rails db:migrate:status`を実行

```
 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20210117044855  Devise create users
   up     20210118033907  Create posts
```

usersテーブルの日付が先になったのでこれでOKです。


あとはファイル編集の内容をローカルリポジトリのmain(master)ブランチに反映させます。

そして再度`git push heroku master`を実行して`heroku run rails db:migrate`を実行したところ、今度はエラーが出ないで無事にデータベースが作成されました！

これで無事にサイトが表示されました🎉
安心しました。

## 補足

**補足でもう１つのエラーについても説明します。**

サイトが無事に表示された後に自分の場合はゲストログイン機能があるのでボタンを押して実行した所、下記のようなエラーが出ました。

<a href="https://gyazo.com/38a585fc213951a8b7e51a4224ddad06"><img src="https://i.gyazo.com/38a585fc213951a8b7e51a4224ddad06.png" alt="Image from Gyazo" width="544"/></a>

ようやくサイトが表示されてホッとしていた所であまり見たことのないエラーが出たのでビビリましたが、調べてみたところ**Cookieを削除すれば直る**との情報を見つけました。
（調べたところ、どうやらRailsのCSRF保護によって発生したエラーらしいので、エラーというよりは仕様みたいです）

そこでChromeの設定からHerokuの自分のアプリケーション名のCookieを削除したら無事に直りました！
（Cookieの削除は各自のお使いのブラウザで行ってください）

良かったです🙌

## 参考サイト

- [[PG::UndefinedTable: ERROR: relation "XXXXXX" does not exist] への対応](https://qiita.com/eitches/items/0144ee436d82822d2535)
- [ログインできない](https://annict.jp/forum/posts/4)
【Android Studio】ActivityNotFoundExceptionを解決した話。(命名に気を付ける）

#Android で画面遷移ができない

画面を変えたいがどうしてもできない。コードを全部見直したけどできない。
なぜできないのかわからずいろいろ試行錯誤したが、原因は単純だった。

エラー：ActivityNotFoundExceptionの原因は、アクティビティの名前をListActivityとしていたことだった。

リストを表示したいからという浅はかかつ単純な理由でこの名前にしたが、じつはこのクラスはandroid.appの下にすでにあった。
Developers->[ListActivity](https://developer.android.com/reference/android/app/ListActivity,"Developers")

###解決法
エラーはおそらく命名のせいで、ListWorkActivityと名前を変えたらちゃんと画面遷移できた。

あとは、インテントの引数でListActivityを指定するときにパッケージを指定してもこのエラーは吐かなくなると思われる。

```java
//名前を変えた場合
Intent intentList = new Intent(MainActivity.this, ListWorkActivity.class);
startActivity(intentList);

//完全修飾名でクラスを指定した場合
Intent intentList = new Intent(MainActivity.this, com.example.appName.ListActivity.class);
startActivity(intentList);
```

非常に初歩的なミスをしてしまった。
というわけで、クラスの命名には気を付けることにしよう。思わぬ罠であった。
データ基盤ざっくりまとめてみた

# はじめに
データ基盤系の勉強をしているので、アウトプットのためにまとめてみようと思います。普段データ基盤に触れていない方や、初学者の方向けの記事になっています。

# データ基盤の役割
まず、データ基盤とは何かという話。

データ基盤はそもそもデータを上手く活用するための仕組みであって、データ活用する時の流れはざっくりこんな感じかと思います。
![スクリーンショット (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/795393/4f2e4e20-bc32-f3d5-bdd6-e557c2da36c1.png)
はじめに企業のビジネス課題からデータの利用目的を決めて、データを収集、分析をして得られた知見から意思決定をしたり色々な使い方をしていきます。ここで重要なのはデータを集めることが目的になってはいけないということ。データ収集や蓄積も、データ分析もそれ自体が目的になると途中で迷うので、必ず何のために行うか明確にしておく必要があります。

データ基盤の場合は、時間をかけてデータを収集・蓄積してもその後活用されないと意味がないので、ステークホルダーまできちんとデータが届き、使ってもらえる仕組みを作ることが大切です。逆にここの部分（データを上手く届ける基盤）が整っていないと、意思決定が遅くなったり質が下がってしまいます。
<br>
もう少し細かく見ていくと、
![スクリーンショット (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/795393/673f50d8-9c2b-1711-6212-f3c099f62ffb.png)
ここのデータを収集してから、データ活用していくまでの間にはいくつか要素があるかと思います。

例えば、

- 利用者が必要なデータを気軽に参照できるツール
- データを安全に流す仕組み
- データを蓄積しておくデータストア
- データを必要な形に加工しておく処理

等が考えられます。

実際にデータ活用していく前段階、それぞれの利用者ごとにデータの参照する場面では、人によって見たいデータが違ってきます。データサイエンティストだったら生のデータから自分で分析したいかもしれないし、マーケターだったらBIツールで今の進捗を簡単に追いたいかもしれません。それぞれのデータ利用者にあった参照方法を提供することで、よりデータを使ってもらえるようになります。

![スクリーンショット (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/795393/f7119cc2-2688-2ae4-147f-826066c8293f.png)


# データ基盤の要素
続いて、データ基盤を構成する要素についてです。大きく3層に分けられます。
<br>
1. <b>データレイク</b>：データソースから収集した生のデータをそのまま格納（構造化データ・非構造化データ関係なく格納）
2. <b>データウェアハウス（DWH）</b>：時系列で、整形されたデータを格納
3. <b>データマート</b>：データウェアハウスよりも具体的に、用途に合わせて整形したデータを格納
![DWH.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/795393/e766ebf5-d2b5-ea6d-ec9e-bec8a7e65cf2.jpeg)

**データソース**から収集されたデータはそのままの形で**データレイク**に入ります。そして、その後整形されて**データウェアハウス**に格納され、用途ごとに応じて整形され**データマート**に格納されます。その間のデータ処理の流れは**ワークフローエンジン**で管理されます。
<br>
データウェアハウス（DWH）は、データを分析しやすく探索できるように蓄積したものです。部門のDBごとに分散したデータ（サイロ化したデータ）を一つに集めて企業の意思決定を支援します。しかし、企業全体のデータが集まるDWHはデータ量も膨大なので、何かを軸に集計しようとするとその分計算量も膨大になります。そこであらかじめいくつかの軸に沿って集計したデータをデータマートに格納しておくことで、データ利用者はその軸について調べれば効率よくデータを活用することができます。そして、DWHやデータマートだけだと、違う集計の仕方をしたい時や生のデータを参照したくなった時に困るので、データレイクで生のデータの保管場所を確保しておきます。
<br>
この3層（特にDWHとデータマート）は境界線が曖昧なので、データの活用方法や規模によって異なる場合もあります。また、データレイク/DWH/データマートというのは役割についている名前で、一般的にDWHとして知られているサービスでこの３層すべての役割を担ってたりもします（GoogleのBigQueryでデータレイク～データマートまでの機能を担う）。

# データソース
もう一つデータ基盤の設計をする時に考えなければいけないのが、どんなデータソースからデータを収集するかということです。

- スプレッドシート
- オブジェクトストレージ
- データベース
- ログファイル

等が挙げられますが、データソースの種類によって収集方法が異なります。各データソースごとにどのような収集方法が考えられるかまとめた分かりやすい[資料][link-3]があったので、是非ご覧ください！


# まとめ
- データ基盤は収集フェーズを速く正確に行うための仕組み（整っていないと意思決定が遅くなったり、質が下がる）
- データがきちんと利用されるように、ステークホルダーの把握やデータが届くまでの流れを整える必要がある
- データ基盤は、データレイク/DWH/データマートの３層で構成されることが多い
- DWHはサイロ化したデータを一つに集めて分析できるようにし、企業の意思決定を支援する
- データソースごとにデータの収集方法が異なる


# 参照
- [Data Engineering Study][link-1]
- [データ基盤の3分類][link-2]
- [データ収集の基本][link-3]

[link-1]:https://www.youtube.com/watch?v=wldg7vDRIAY
[link-2]:https://speakerdeck.com/yuzutas0/20190213
[link-3]:https://www.slideshare.net/tetsutarowatanabe/japantaxi-238047234
ElectronではJavascriptのwindow.promptが使用出来ません。

私はプログラム初心者なのですが、Javascriptで、ある程度動作するWebアプリを作成し、PCでも動作するようにElectronも勉強中です。
しかしながら、Electronでビルドした後に、Javascriptで書いたpromptの機能が動作しない事に気づき、あわててググりましたが、日本語のサイトには情報が少ないので投稿させていただきます。
てか、当たり前のことなのかな？

ElectronではJavascriptのwindow.promptが使用出来ません。まあnot supportedと表示されるので間違いなく動作しないのですが、私の様に初心者の方が気軽にJavascriptで書いたコードがElectronで動くからとGoogle Chromeで確認しながらコードを書いていると、後で泣きをみるかもしれないのでお伝えしておきます。
しかし、この問題というか、この仕様以外にどれほどの影響を受けるのか？は全くわかりませんので、ある程度認識した上で開発を進めるとショック度が少ないかと思います。

代替え案
window.promptはセキュリティ上の問題でサポートされていないようですが、いくつか代替えのノードモジュールがあるようです。

私の環境ではsmaltalkは動作しませんでした。
https://www.npmjs.com/package/smalltalk

electron-promptで代替えできることを確認しました。
https://www.npmjs.com/package/electron-prompt

但し、ノードモジュールはメインプロセスでしか動作しないようなので、ユーザー操作や入力情報などをレンダラープロセスからメインプロセスに送信して、またメインプロセスから入力内容を受診するというややこしいことをしなくてはなりません。そこはipc通信やremoteなどでググってくださいね。ただこれもセキュリティ上、メインプロセスで直接指定しない方が良いようです。
が、また一段かますのが...node integration true...悪魔の呪文

ど素人の私はレンダラープロセスにconst prompt = require('electron-prompt');を貼り付けたりして動かんなぁーと右往左往していました^^;
Matplotlibで軸はベクターのままプロットだけラスタライズする

論文のFig1とかでexample traceが激重で全然表示されずイライラ、ということは多いけど
いざ自分でmatlabでごりごりとプロットすると、重いプロットだけラスタライズするのはとても面倒だった。

そんな面倒なラスタライズも簡単にできる。そう、pythonならね　（ネタが古い）。

#やりたいこと
上に書いた通り、散布図でもなんでも良いのだけど、プロットだけラスタライズして
それに乗っているテキストや軸などはベクターのまま持ってpdfなどに出力したい。

簡単な方法を以下で見つけたので自分用メモ。
https://oku.edu.mie-u.ac.jp/~okumura/python/rasterize.html

#やりかた
とりあえずmatplotlibを使う前提で。
１つのプロットを全部ラスタライズするなら、```ax.set_rasterized(True)```で良い。
が、これだと軸などもラスタライズされてしまう。

そこで、``` ax.set_rasterization_zorder(z)```というヤツを使う。
これは、zorderが引数より小さいプロット要素だけをラスタライズする、というコマンド。
こいつを上手く使うために、プロットするときに```zorder```というプロパティを仕込んでおく。

```python
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ax.scatter(x, y, zorder=-10)    #このzorderがポイント
ax.set_rasterization_zorder(-5)
```

といった感じ。あとは出力するときに```dpi=300```くらいに設定しておけば、そこそこキレイでまぁまぁ軽いプロットができる。


#余談
ちなみにMatlabでは良い方法が見つからず、一度プロットのサイズぴったりのfigureを作ってプロットし
```axis off```にしてから画像ファイルに書き出して
もう一度そのファルを読み込んで軸と一緒にPDFに出す、という面倒くさいことをやったりしていた。
（描画されている絵を直接取り込むと解像度がとても低かった）

matlabであんなに苦労したことがこんなに簡単にできて、ちょっと感動してしまった。
【初投稿】リセットCSSを書き記す

昨日から初めての`WEB開発`をスタートさせました！
にしても初めてはわからないことばかり、、、、

今回はリセットCSSを設定し忘れ、意味不明なCSSが反映されている方向けに
コピペで使えるリセットCSSを書き記したいと思います。

**▼リセット！**

```
*{
margin: 0;
padding: 0;
}
html{
font-size: 16px;
}
body {
font-family: Verdana, "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, sans-serif;
}
header, footer, nav, menu, article, aside, section, details, figcaption, figure{
display: block;
}
ul, ol {
list-style: none;
}
table {
border-collapse: collapse;
}
img {
vertical-align: bottom;
}
a img {
border: none;
}
strong {
font-weight: normal;
}
i{
font-style: normal;
}
```

以上！どこから拾ってきたソースコードなのか、後日記載できればと思います！
では、これにて一旦ドロンです

マイベストフレンド @norinori_stu2 ありがとう！
cesiumの作法 その6

#概要

cesiumの作法、調べてみた。
three.js使ってみた。

#サンプルコード

```

var viewer = new Cesium.CesiumWidget('cesiumContainer', {
});
var scene;
var box;
var camera;
var renderer;
var light;
var width = window.innerWidth;
var height = window.innerHeight;
scene = new THREE.Scene();
box = new THREE.Mesh(new THREE.BoxGeometry(50, 50, 50), new THREE.MeshLambertMaterial({
  color: 0xff0000
}));
scene.add(box);
light = new THREE.DirectionalLight(0xffffff, 1); 
light.position.set(0, 100, 30);
scene.add(light);
camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
camera.position.set(200, 100, 300);
camera.lookAt(box.position);  
renderer = new THREE.WebGLRenderer({
  alpha: true
});
renderer.setSize(width, height);
document.getElementById('threeContainer').appendChild(renderer.domElement);
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
render();




```

#成果物

https://embed.plnkr.co/plunk/1KwmelpkcFwrXIBg

以上。
Blenderに3D地図情報に出現!!

#目的
Blender上にて、3D地図情報をインポートします。

!注意!
__建物__の情報だけBlender上に出現します。

#環境
* Blender 2.83.7

#目次
1. OSMのダウンロード
2. Open Street MapからBlenderにエクスポート
3. 作成動画

# OSMのダウンロード
まず初めに、Blenderに__OSM__のインストールを行います。

※OSM（オープンストリートマップ）とは、自由に利用でき、なおかつ編集機能のある世界地図を作る共同作業プロジェクトであります。

https://github.com/vvoovv/blender-osm/wiki


![2021-02-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/51c20a50-e8ab-7de4-046e-deabce0741d8.png)

「Name a fair price」と書いてありますが、任意で適正価格を付け入る事が出来ます。
![2021-02-21 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/db0673a0-15a9-7117-4613-91c1acbb92b9.png)

![2021-02-21 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/4cd50bb5-5676-e5c1-f496-e18eb16bee41.png)

![2021-02-21 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/3e949a0f-6ff4-3368-ce39-f67f5fb5ff1b.png)
Downloadをクリックします。
![2021-02-21 (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/83b03ff9-111c-1399-1ad1-f490ed5c8da2.png)


# OSMの設定
Blenderの左上の「編集」から「プリファレンス...」「アドオン」「インストール」の順番に選択していきます。

![OSMの設定.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/266635af-7299-9d8d-8620-0341822faee7.jpeg)


先ほど、ダウンロードした「blender-osm.zip」を選んでアドオンにインストールします。

![2021-02-21 (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/8251ad83-fdb8-a0c4-ba1d-0c2e60a98175.png)

右上の検索タブから、「__OSM__」と検索します。
出てきた「Import-Export: blender-osm」にチェックマークを入れます。
任意の保存先に保存します。
![2021-02-21 (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/f36c58ea-9d0d-6011-4bb3-61467b46a86c.png)

osmが追加されている事を確認します。
![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/19af9939-c891-5bb7-5f58-66e6d50e1d21.gif)
![2021-02-22 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/c2b4c6c0-0944-191f-de97-4d3dca524918.png)

__これで、OSMの設定は終わりです。__


#2.Open Street MapからBlenderにエクスポート
Open Street Map(OSM)から好きな場所に移動します。
今回は、東京駅にします。

https://www.openstreetmap.org/?edit_help=1#map=5/38.359/141.387

![2021-02-22 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/1f3ab72d-ca0c-50d0-7243-2dab6f7e2bec.png)

![2021-02-22 (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/64ab3ccd-7820-e1fd-7e4f-300fc6240ee3.png)

先ほどのBlender　osmに座標を入力します。
入力したら__import__します。
![2021-02-22 (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/5d24d3a5-60f2-00a0-4707-51c6426bdb2f.png)

画面上に東京駅が出現します。


# 3. 作成動画
下の画像を選択すると動画が再生されます。
YouTubeに飛びます。
[![ビデオが開けなかった場合に表示されるテキスト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675881/dd4a503a-720b-99a3-3f0a-b0cd4d0a430c.png
)](https://youtu.be/jc3OB3Bt1as)
【Django】CodePipeline デプロイ成功もアプリ起動せず

![スクリーンショット 2021-02-22 15.54.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146151/b714b5da-67b9-d545-adc5-46d48d84cb11.png)

画像の様にパイプラインでデプロイが成功したにも関わらずアプリが起動しない・・・

なので、SSHでインスタンスに入って

```
/var/log/django-application-stderr.log
```
を確認したところ

```
ModuleNotFoundError: No module named 'dateutil'
```

エラーが吐かれていました。


なので、requirements.txtに

```
python-dateutil==2.8.1
```

を追加して再度デプロイ。

無事に開きました。
macでGIFアニメをサクッと作る最高のツール

エンジニアだとMarkdownを使ったりとかSlackでコミュニケーションしたりとか頻繁にしますよね。そこで手軽にアニメーションを共有する手段としてGIFアニメが大変便利なため重宝しますよね！私もGIFアニメよく使うのですが、QuickTime Playerで画面録画をしたmovファイルをGIFに変換するのってみなさんどんな手段でやってますか？

macでGIFアニメを手軽に作るアプリとしては、[PicGIF](https://apps.apple.com/jp/app/picgif/id784260229)が有名ですが、画像の縦横サイズの編集が手軽にしづらかったり、繰り返し再生回数を指定できなかったり（とりわけ無限ループにできない）、各コマのalignmentが調整できなかったり、GIFのトリミングができなかったり、痒いところに手が届かないと感じていました。また、Effectや文字列追加機能は不要だと思っていたため、必要十分な機能のアプリを作ることにしました。

# GIFlash

![Screen Shot 2021-02-22 at 15.30.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67153/2368fee6-77db-b9a9-ce55-7b0132a3aa21.png)
![Screen Shot 2021-02-22 at 15.37.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67153/1284b9a3-4764-5988-276b-b791854c2e3c.png)

## 特徴

1. 複数のコマ画像をインポートしてパラパラ漫画を作れる
2. 動画からコマ画像を切り出してインポートできる
3. 繰り返し再生（ループ）の回数を指定できる
4. GIFアニメのサイズの変更が容易  
(アスペクト比をロックして拡大縮小できる)
5. サイズの異なるリソースを用いる場合、中央揃えや右寄せ、上寄せなど配置の指定ができる
6. 背景色の指定ができる(透明も可）
7. 既存のGIFアニメのトリミングができる
8. 編集中の各コマをPNGとして出力できる

![Screen Shot 2021-02-22 at 15.39.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67153/e710db5f-8a41-b26f-2224-e7405d3e6abb.png)

# 技術的な話

## NSImageの配列からGIFを作って保存する方法

- `CGImageDestinationなんたら`をつかっていく。
- 繰り返し回数は0を指定すると無限ループになる
- `kCGImagePropertyGIFDelayTime`という属性と`kCGImagePropertyGIFUnclampedDelayTime`という属性があるが、前者だとフレーム間隔の最小が100msまでしか指定できない。後者を選ぶともっと間隔を短くできる。

```swift
import Cocoa
import ImageIO

var frames = [CGImage]() // フレームを追加しておく

func generateGif(_ saveURL: URL, // NSSavePanelとかでユーザに保存場所を指定させる
                 _ loopCount: Int, // 繰り返し再生回数
                 _ interval: Float, // フレーム間隔
                 successHandler: () -> Void,
                 failureHandler: () -> Void) {
    guard 0 < frames.count,
          let gif = CGImageDestinationCreateWithURL(saveURL as CFURL, kUTTypeGIF, frames.count, nil)
    else { return failureHandler() }
    // 繰り返し再生回数の指定
    let props = [
        (kCGImagePropertyGIFDictionary as String) : [(kCGImagePropertyGIFLoopCount as String) : loopCount]
    ]
    CGImageDestinationSetProperties(gif, props as CFDictionary)
    // フレーム間隔の指定
    let frameProps = [
        (kCGImagePropertyGIFDictionary as String) : [(kCGImagePropertyGIFUnclampedDelayTime as String) : interval]
    ]
    // フレームを追加していく
    frames.forEach { (frame) in
        CGImageDestinationAddImage(gif, frame, frameProps as CFDictionary)
    }
    // 出力する
    if CGImageDestinationFinalize(gif) {
        successHandler()
    } else {
        failureHandler()
    }
}
```

## そのほかハマったところ

- [Swift：CGImage ←→ NSImage ←→ CIImage【変換Extension】](https://qiita.com/Kyome/items/87b771e13695a6fba99e)
- [Swift: 画像をプログラムで描画するときのTips](https://zenn.dev/articles/1f80d6cba672c4/edit)
- [Swift: macOSでの座標系のややこしい話](https://zenn.dev/articles/6d55d686069b0c/edit)
- [Swift：実践的なUndoManagerの使い方](https://qiita.com/Kyome/items/160c582a9a942e5e119b)
- [Swift：AVPlayerでシンプルな動画プレイヤーをつくる](https://qiita.com/Kyome/items/4357f1c010c678f7c451)

# リンク
- [GIFLash](https://apps.apple.com/jp/app/giflash/id1507246566?mt=12)
  **使ってみてのコメント待ってます🙏**
docker をrestart しようとした時の、too quickly ~~　と言われて起動できない問題解決備忘録

## 概要

よく解決方法がわからずハマってしまいました、、

## 解決方法
いろいろ記事が出てきますが、

https://www.hiroom2.com/2017/02/18/linux-systemd%E3%81%AEstart-request-repeated-too-quickly-for-xxx-service/

この記事で書かれている


```
sudo systemctl daemon-reload
```

を実行した後に

```
sudo systemctl restart docker
```

であっさりと解決しました。。
【kintone x OCR】OCRシステム　ユーザー登録【GCP】

# はじめに
OCRシステムを構築しました！
30日間無償で利用できますので、是非お試しください！！

# 出来ること・画期的なこと
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/chari_tarou/status/1363655562186035200?s=20"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

1. OCR結果から必要な情報だけを取得できます。
1. 変換元画像と変換結果を並べて比較できます。
2. アカウント登録すれば、すぐに利用できます。

# アカウント登録
下記に「アカウント申請」という件名（本文なし）でメールを送付頂ければ、数分程度でアカウント情報を返信します。

ocr.lastonemile@gmail.com
* 利用規約に同意の上、ご利用ください。　https://tanoshimane.stores.jp/news/603313afc19c4527490eb72e
* kintoneというクラウドアプリ上に構築しており、そのゲストIDを発行します。
* 30日間又は100ページまで（kintoneの利用料含めて）無償で利用可能です。

# 利用方法
OCRシステム　Vision API arr.by LOM 利用方法: https://qiita.com/Lastonemile/items/0651e9845410f12fc660

# 注意事項
* **IEではご利用いただけません**（Chrome,edgeは動作確認済、その他ブラウザは未検証です）
* モバイルでご利用の場合は、PC画面でご利用ください。 https://jp.cybozu.help/k/ja/user/mobile_access/mobile_view.html
* 処理の一部をAWSで実施（kintoneからAWS（api Gateway）しています。
* Google Cloud Visionを利用しています。
* 利用規約に同意ください。　https://tanoshimane.stores.jp/news/603313afc19c4527490eb72e

## 筆者の紹介
群馬県の渋川市・高崎市を中心に群馬県内でkintoneを利用したDX化支援を行っています。ご興味ある方はぜひご連絡ください。
https://ks-kiki.hp.peraichi.com/
自分と折り合いをつけるアプローチ（他人は変えられないが自分は変えることができる）

プログラムとは直接的な関係がないのでこちらに移動しました。

[自分と折り合いをつけるアプローチ（他人は変えられないが自分は変えることができる）の備忘録](https://plussum.hatenablog.com/entry/2021/06/06/172423?_ga=2.166708925.2086076410.1622962263-1594190019.1614326990)
web storyに最低限必要なソースをまとめてみた

# web story (旧amp storyとは)
インスタで人気になった画像や動画中心の投稿機能を、WEBでも使えるようにしたものです。
AMP(Accelerated Mobile Pages)の機能としてリリースされました。
通常のHTMLほど自由度がないですが、そのかわりに簡単に実装することができます。

## インスタやTwitterのストーリーと異なる点
1. アプリのインストールが不要
1. PC、タブレット、スマホで閲覧が可能
1. WEBページ(HTML)なのでGoogle検索にHITする

[SEO視点から見た「Webストーリー」とは？（機能や活用メリット）](https://www.youtube.com/watch?v=-hXWKcX4I8I)というYoutube動画で詳しく説明されています。

# 必要最低限のソース

```html
<!doctype html>
<html amp lang="ja">
<head>
	<title>hogehoge</title>
    <meta charset="utf-8">
    <link rel="canonical" href="http://localhost.com/amp/hogehoge.html">
    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
	<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
	<script async src="https://cdn.ampproject.org/v0.js"></script>
	<script async custom-element="amp-story" src="https://cdn.ampproject.org/v0/amp-story-1.0.js"></script>
</head>
<body>
<amp-story standalone
           title="hogehoge"
           publisher="fugafuga"
           publisher-logo-src="./hogehoge.jpg"
           poster-portrait-src="./hogehoge.jpg">
    <amp-story-page id="first-page">
    	<amp-story-grid-layer template="fill">
    		<amp-img src="./hogehoge.jpg" height="1280" width="720"></amp-img>
    	</amp-story-grid-layer>
    	<amp-story-grid-layer template="vertical">
    		<h2>1ページ目のコンテンツ</h2>
    	</amp-story-grid-layer>    	
    </amp-story-page>
</amp-story>
</body>
</html>
```

AMPテストというサイトで構文が正しいかを確認できます。
[https://search.google.com/test/amp](https://search.google.com/test/amp)

## 表示の仕組み
以下で説明されています。(画像は同ページから転載)
https://amp.dev/ja/documentation/guides-and-tutorials/start/visual_story/parts_of_story/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533120/706729e5-de13-7315-b262-26baa99c8f08.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533120/d5c8b95e-50c7-3ff9-ade6-a83a39e54a5f.png)

## その他 web storyでできること

以下にweb storyでできること(使えるタグ)が一覧でまとまっています。
https://amp.dev/documentation/components/amp-story-page/?format=stories
【kintone x OCR】OCRシステム　Vision API arr.by LOM 利用方法【GCP】

#出来ること
kintone上で様々な書類から必要な値を取得します。

例として、この↓画像（請求書）から、請求番号・支払期限・振込先・請求元・明細（品名・数量・・・のテーブル部分）の値を取得することが出来ます。

＊後述する文書テンプレートを作成することで様々な書類から必要な値を取得できます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/chari_tarou/status/1363655562186035200?s=20"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

![請求書サンプル.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543387/13655cba-1ef7-40ba-748c-135bc5af3280.jpeg)
↓
取得結果（kintone画面）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543387/3d9e69ea-a44b-ad68-967c-70a431d353cf.png)

#ユーザー登録
下記の記事より、ユーザー登録してください。
https://qiita.com/Lastonemile/items/65b9102c868e09b3248e

#事前準備（文書テンプレート作成方法）
事前に文書テンプレートを文書（帳票）ごとに、同梱のTemplate assistアプリ及び文書テンプレートアプリで作成する必要があります。（文書テンプレートを設定しない場合には、「変換結果（全データ）」（Cloud Visionの結果をcsv形式に変換したファイル）のみ利用できます）

文書テンプレート作成方法：https://qiita.com/Lastonemile/items/522336f667747a888b17

#アプリの利用方法
1. レコード追加画面で、対象画像がJPEG,PNGであれば「対象ファイル(png or jpeg)」に、PDFであれば「対象ファイル(pdf)」に添付してください。
＊PDFファイルの場合は、保存時にJPEGファイルへ変換します。

2. 対応する文書テンプレートを選択します。（文書テンプレートを設定しない場合には、「変換結果（全データ）」（Cloud Visionの結果をcsv形式に変換したファイル）のみ作成されます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543387/1529af91-c7d9-cc62-4146-bf8ca20614c1.png)
3. 保存しレコードの詳細ボタン画面で「OCR変換」ボタンを押下します。
4. 数秒～数十秒後、画面が自動更新され画面下方にOCR結果が表示されれば成功です。
![TEMPLATE5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543387/4d4702ec-8e25-7947-5330-ad14610b4ec9.png)
5. 登録結果を他アプリに登録する場合には、登録ボタンを押下します。（要別途開発）
マークダウンの練習と、初めまして。

#こんにちは
こんにちは。マークダウン式というのを初めて本格的に使ってみました。hackmdなどのサイトでほかの方の見様見真似で多少やったことはあるのですが、これはなかなか面白いですね！

[こちら](https://qiita.com/Qiita/items/c686397e4a0f4f11683d#code---%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%8C%BF%E5%85%A5 "Markdown Qiita")の記事を見ながら練習しています。

## これがH2タグ
Hello everyone!! 

コードを挿入してみます。

```Java:practice.java
System.out.println("Hello, world!")
```

ここで少し自己紹介をすると、私はJavaを勉強しています。大学の授業で少しPythonを学んだので、Pythonの基礎的なところもわかりますが、基本はJava使いです。
現在の目標はAndroid Studioで作っているアプリを完成させることです。アプリはほぼ自分用で公開目的ではありませんが、春休み中、三月いっぱいをめどに完成させたいです。

あと、余裕があればxmlかhtmlでデザイン的なものも学びたいし、GASもやりたい。Slackで自由自在にbot導入できたらつよつよじゃないですか。
もっと欲を言うと、ゲームを作りたいです。すっごい面白いゲームを。でもこれは最終目標なので、とりあえずは一つアプリを開発せねばならんと思っているのです。今作ってるやつを。知らないことが多すぎるんで、できるところから頑張ります。

#こんばんは
**太字**と*Italic*。（アスタリスク）
__太字__と_Italic_。（アンダーバー）
アスタリスクのほうが使いやすい。
##調子はどうだい？
打消し線
~~楽しい~~
###ああ、すごくいいよ。ちなみにこれはH3タグだ。
detailsタグを使ってみました。
<details><summary>今日は猫の日です。</summary>今日は猫の日です。猫はにゃーにゃーと鳴きますね。2月22日はにゃーにゃーにゃーなので、猫の日なのです。来年は2022年なので、にゃー×6で、ねこねこの日ですね。</details>

もう一度コード挿入の練習。コード挿入の時は上下に空行を入れる。

```markdown:cat.md
<details><summary>今日は猫の日です。</summary>今日は猫の日です。猫はにゃーにゃーと鳴きますね。2月22日はにゃーにゃーにゃーなので、猫の日なのです。来年は2022年なので、にゃー×6で、ねこねこの日ですね。</details>
```

####それはよかった。H4.
***
horizontal lines.
***

#####そこの居酒屋でもどうだい。H5
Texも使えるみたいですね。やり方よく知らないけど。

```math
\
D = P^{-1} A P
\
```

######ああ、いいね、そうしよう。H6
H5とH6ってあんま変わらないように見えますね。そこまで深く分類することもないしあんま使わなそう。
Pythonで学ぶ制御工学　第5弾：ブロック線図

# #Pythonで学ぶ制御工学< ブロック線図 >

## はじめに
基本的な制御工学をPythonで実装し，復習も兼ねて制御工学への理解をより深めることが目的である．
その第5弾としてブロック線図を扱う．

## ブロック線図
システムを図示する際によく用いられるもの．例えば，入力が$u$で出力が$y$のシステム$S(y=Su)$をブロック線図で表すと，次のようになる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/a7bdd915-63c9-d4f0-6ba6-cf261563484e.png)

複数のシステムをどのように組み合わせるかによりその表現は様々である．以下に代表的なものを示す．

#### 直列結合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/df44bd7b-b4c4-57bb-5f46-9695379a783e.png)
このとき，システム全体$S$は次のように求められる．
$S = S_1 \times S_2$

#### 並列結合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/25757765-9358-5329-930e-1e7530e085c9.png)
このとき，システム全体$S$は次のように求められる．
$S = S_1 + S_2$

#### フィードバック結合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/444abcab-0b23-034d-9c01-1eae20a35e02.png)
このとき，システム全体$S$は次のように求められる．(詳細には，誤差など各箇所で方程式を立て連立方程式を解くことで求められる．公式として覚えるものではないとは個人的に思う．)
$S = \frac{S_1}{1 + S_1 S_2}$

## 実装
ここでは，Pythonで全体のシステムを計算するプログラムを学ぶ．以下にソースコードとそのときの出力を示す．プログラムの説明はソースコード内にコメントしている．なお，全体のシステムの計算は数値計算であるため，ここでは適当なシステム2つに対して実行している．

##### ソースコード
```python:block.py
"""
2021/02/21
@Yuya Shimizu

ブロック線図
"""
from control import tf, series, parallel, feedback

##例で使用する2つのシステムを生成
#numerator: 分子　denominator: 分母
num1 = [0, 1]
den1 = [1, 1]
S1 = tf(num1, den1)

num2 = [1, 1]
den2 = [1, 1, 1]
S2 = tf(num2, den2)


##直列結合
S = S1 * S2
print(f"<直列結合>\n(式より){S}")
S = series(S1, S2)
print(f"(series関数より){S}\n")


##並列結合
S = S1 + S2
print(f"<並列結合>\n(式より){S}")
S = parallel(S1, S2)
print(f"(parallel関数より){S}\n")


##フィードバック結合
S = S1 / (1+ S1*S2)
print(f"<並列結合>\n(式より){S}\n\t↓約分\n{S.minreal()}")    #Pythonではうまく約分されない　約分するには，minrealメソッドを使う
S = feedback(S1, S2)
#フィードバック部分にシステムがない場合は，第2引数を1として考える，もしくは第1引数のみで実行する．
#また，ポジティブフィードバックの場合は，第3引数目sign=1と指定する
print(f"(feedback関数より){S}\n")
```

##### 出力
```
<直列結合>
(式より)
        s + 1
---------------------
s^3 + 2 s^2 + 2 s + 1

(series関数より)
        s + 1
---------------------
s^3 + 2 s^2 + 2 s + 1


<並列結合>
(式より)
   2 s^2 + 3 s + 2
---------------------
s^3 + 2 s^2 + 2 s + 1

(parallel関数より)
   2 s^2 + 3 s + 2
---------------------
s^3 + 2 s^2 + 2 s + 1


<並列結合>
(式より)
    s^3 + 2 s^2 + 2 s + 1
-----------------------------
s^4 + 3 s^3 + 5 s^2 + 5 s + 2

	↓約分

     s^2 + s + 1
---------------------
s^3 + 2 s^2 + 3 s + 2

(feedback関数より)
     s^2 + s + 1
---------------------
s^3 + 2 s^2 + 3 s + 2

```

## controlライブラリ
今回使用した3つの結合に対する関数はcontrolライブラリの中にあり，それぞれ表にまとめておく．使用方法については，ソースコードを参照されたい．

| 直列結合 | 並列結合 | フィードバック結合 |
|:-----------:|:------------:|:------------:|
|series( )       |parallel( )       |feedback( )        |


## 感想
ブロック線図から全体のシステムをプログラムで求めることはないが，数値計算という点においては活用の機会はありそうである．次回は，状態空間モデルと伝達関数モデルの関係性についてまとめる．


## 参考文献
Pyhtonによる制御工学入門　　南 祐樹　著　　オーム社
⚠️【nodist】`npm i`実行時の`cb.apply is not a function`【Windows環境】

nodistが`.node-version`ファイルを参照したり、自分でNode.jsのカレントバージョンを変更したりすると、Node.jsと`npm`のバージョンがずれてしまうことがあります。

```
npm ERR! cb.apply is not a function

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\*****\AppData\Roaming\npm-cache\_logs\yyyy-mm-ddTXX_XX_XX_XXXX-debug.log
```

そういうときは、次のコマンドを叩くかnpmをインストールしなおして`npm`コマンドのバージョンをNode.jsのバージョンにそろえてあげます。

```shell
nodist npm match
```

これで無事に`npm`コマンドが利用できるようになります。
もしこれでも改善されない場合は、npmを再インストールしてください。

そもそも`nodist npm match`が実行できない場合はtaketakekahoさんの[nodistでnpmとNode.jsのバージョンが合わせられずエラーとなった](https://qiita.com/taketakekaho/items/655e63c35a9467f42afa)をご参照ください。

-----

- [nodistでnpmとNode.jsのバージョンが合わせられずエラーとなった - Qiita](https://qiita.com/taketakekaho/items/655e63c35a9467f42afa)
- [nullivex/nodist: Natural node.js and npm version manager for windows. - GitHub](https://github.com/nullivex/nodist#readme)
Email Studio の基本 ⇒ Email Studio の使用開始

１．次のうち、Email Studio でできることはどれですか?
２．マーケティングアセットを作成して共有できるのはどのツールまたはタブですか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/2dbacea4-b14f-02ee-03e5-2c7315b47fe5.png)
GIT HUBに草が生えない時の対処法

今回で記事を書くのも4回目になってきました。
しかし、まだまだ慣れません！！

今回は、GIT HUBの草が生えない時の対処の仕方について
記事を書きたいと思います。

そもそも、草を生やすって?
と思う方もいると思います。

GIT HUBには、ユーザーがどのくらいGIT HUBのリモートリポジトリに
反映されているかがわかるページがあるます。

通常、Git Hub Desktopから、ブランチがmasterの時に、commit→pushを
行われれば、記録が反映されます。

[![Image from Gyazo](https://i.gyazo.com/2063a166cac92a50da62b970d3c7384b.png)](https://gyazo.com/2063a166cac92a50da62b970d3c7384b)

上記の画像の、83 contributions in the last year
の下の部分の四角いブロックの表が記録になります。
白いところが何も更新していないところで、緑が濃くなるほど
Git Hubを更新したところになります。
私は、毎日更新していましたが、ある時から突然反映されなくなりました。
かなりショックです・・・・。

しかし、原因を調べた結果解決できました。
今回の原因は、ローカルとGit Hubのメールアドレスの不一致が原因でした。

##  Github のメールアドレスを確認

まずは、画面右上部の小さくプロフィール写真が表示されているところ
をクリックしてそこから、「settings」のページに移動します.

次に「Account settings」の中から、「Emails」の
ページに移動します。
すると、登録しているアドレスが確認できます。

## ローカルのメールアドレスの確認・変更

次はローカルのアドレスの確認です。
これは、ターミナルから確認ができます。


```
git config user.email
```

上記のコマンドを実行しアドレスが確認できます。
もし、Git Hubに記載されていたものと違ったら変更が必要です。
また、コマンド実行後何も表示されなかった場合は登録がされて
いないので登録が必要となります。

変更は下記のコマンドで実行できます。

```
git config user.email test@test.com
```

test@test.comがご自身のアドレスを入力する部分になります。
この後に、コミット→プッシュを行うと反映されます。

以上が、私がGit Hubに草が生えなかった原因と対処法でした。
私の場合同じemailアドレスを登録していると思ったのですが、
スペルミスが原因でした。
確認は大切ですね！！

まだまだ、初心者ですので何か間違いがございましたら
ご指摘いただければ幸いです。
よろしくお願いします。
ROSの勉強　第12弾：センシングと移動

# #プログラミング ROS< センシングと移動 >

##はじめに
1つの参考書に沿って，ROS(Robot Operating System)を難なく扱えるようになることが目的である．その第12弾として，センシングと移動を扱う．


##環境
#####仮想環境
|||
|:-:|:--|
|ソフト|VMware Workstation 15|
|実装RAM|2 GB|
|OS|Ubuntu 64 ビット|
|isoファイル|ubuntu-mate-20.04.1-desktop-amd64.iso|

#####コンピュータ
|||
|:-:|:--|
|デバイス|MSI|
|プロセッサ|Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz 2.50GHz|
|実装RAM|8.00 GB (7.89 GB 使用可能)|
|OS|Windows (Windows 10 Home, バージョン：1909)|

#####ROS
|||
|:-:|:--|
|Distribution|noetic|
|プログラミング言語|Python 3.8.5|
|シミュレーション|gazebo|


#タスク
前回と前々回でセンシングと移動について学んできた．今回はそれらを使って，あるタスクを通して組み合わせについて理解を深める．以下にそのタスクのフローチャートを示す．
![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/dbfb502d-c17c-6f18-4046-ce960b899034.png)

距離の部分をセンサで計測し，それによる移動を行う．



##実装
以下にソースコードを示す．なお，このプログラムを実行する際には，gazebo上でturtlebotを起動させた状態で行っている．

```python:wanderbot.py
#! /usr/bin/env python3

import rospy
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan

def scan_callback(msg):
	"""読み取ったセンサデータをグローバル変数に渡すことで，プログラム全体でその情報を扱えるようにする"""
	global g_range_ahead
	g_range_ahead = msg.ranges[len(msg.ranges)//2]	#正面の距離をグローバル変数に格納


g_range_ahead = 1	#初期値
scan_sub = rospy.Subscriber('scan', LaserScan, scan_callback)	#センサデータの購読
cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size = 1) #移動トピックの配信準備

rospy.init_node('wanderbot')	#ノードの初期化

state_change_time = rospy.Time.now()	#現在時刻を記録
driving_forward = True	#直進可能と仮定
rate = rospy.Rate(10)	#10Hz

#ctrlキーが押されるまでループ
while not rospy.is_shutdown():

	#直進可
	if driving_forward:
		#条件1と2を満たすか
		if (g_range_ahead < 0.8 or rospy.Time.now() > state_change_time):
			driving_forward = False	#直進不可
			state_change_time = rospy.Time.now() + rospy.Duration(5)	#rosの基準で5duration先の時刻を現在時刻とする　→　5durationの間回転を続けるということ

	#直進不可
	else:
		#方向転換が終了したか
		if rospy.Time.now() > state_change_time:
			driving_forward = True	#直進可能
			state_change_time = rospy.Time.now() + rospy.Duration(30)	#rosの基準で30duration先の時刻を現在時刻とする　→　30durationの間直進を続けるということ


	twist = Twist()	#全体を0で初期化するために毎回Twistインスタンスを生成	これにより，以下では動かしたい要素のみの変更で済む

	#直進可能　→　直線方向の速度1
	if driving_forward:
		twist.linear.x = -1
	
	#直進不可　→　方向転換　→　回転の速度(z軸周り)1
	else:
		twist.angular.z = 1

	cmd_vel_pub.publish(twist)	#移動トピックを配信

	rate.sleep()	#10Hz(=0.1秒)待つ
```

##結果
結果としては，あちこち移動するようなロボットが完成した．その様子を以下に示す．
![ezgif-7-2f690b5ec445.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/766ee356-36fe-4755-90af-6332f2d802a2.gif)

##感想
前々回と前回で学んできた``twist``による移動と``LaserScan``を用いたセンシングをどのように組み合わせるかということをwanderbotを使って学んだ．特にセンシングにより読み取ったデータは**グローバル変数**に格納することで，条件にそのデータを用いたりすることができ，それにより条件付きの移動プログラムの実現が可能であることを確認できた．

##参考文献
プログラミングROS Pythonによるロボットアプリケーション開発
　　　　　　　　Morgan Quigley, Brian Gerkey, William D.Smart 著
　　　　　　　　　　　　　　　　　　　　　　　河田　卓志　監訳
　　　　　　　　　　  　松田　晃一，福地　正樹，由谷　哲夫　訳
　　　　　　　　　　　　　　　　　　オイラリー・ジャパン　発行
Python: 超立方体の展開

# はじめに

超立方体は単なる2進数だった。この話は以下の記事で行っているので、この知識を前提に超立方体を展開していく。

[python: 超立方体](https://qiita.com/sai-sui/items/badca9920ce76886322b)

# 準備

まずは準備

``` python
import random
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def extension(dim):
    """ 次元を拡張する関数

    :param dim: 拡張する次元の配列
    :returns: 拡張された配列
    """
    return np.concatenate([np.array(dim) - 1, np.array(dim) + 1]).tolist()


dot = [[0], [0], [0], [0]]
```

# 直線
そしてこれが点から直線(1次元)への拡張だった。直線の数はわかりやすく 0 -> 1 の変化の数である（1 -> 0 も直線だが重複を許すことになるので、この変化は無視する）。当然のことで、以下の直線は1つである。

``` python
line = [extension(dot[0]), dot[1]*2, dot[2]*2, dot[3]*2]
for x, y, i in zip(line[0], line[1], range(len(line[0]))):
    plt.scatter([x], [y], marker="o", linestyle='None', label=str(i)+' : '+format(i, '0'+str(1)+'b'))
plt.legend()
plt.show()
```

![直線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365961/85c63733-401f-30a0-8711-62758e5a1004.png)

# 正方形
そしてこれが正方形(2次元)への拡張だった。

``` python
square = [line[0]*2, extension(line[1]), line[2]*2, line[3]*2]
for x, y, i in zip(square[0], square[1], range(len(square[0]))):
    plt.scatter([x], [y], marker="o", linestyle='None', label=str(i)+' : '+format(i, '0'+str()+'b'))
plt.legend()
plt.show()
```

![平面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365961/01b5da82-b8f4-7143-1bf3-b2c0aba6f37c.png)


ここから直線は何個取れるだろう。取り方は、1つの次元を固定して、残りの次元で 0 -> 1 の変化になっている個数を数えればよい。つまりは1次元目を 0（赤枠） or 1（青枠） に固定して得られる 2個 と

![展開1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365961/38e685ef-1559-5aa8-9c78-5e85e825c445.png)

2次元目を 0（赤枠） or 1（青枠） に固定して得られる 2個 である。(赤枠、青枠はラベル内の枠の事…伝わりにくかったらごめんなさい)

![展開2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365961/19cc384c-0a38-67c7-2694-e9857e176d6a.png)

そして平面は 00 -> 01 -> 10 -> 11 の変化である。

# 立方体
そしてこれが立方体への拡張だった。

``` python
cube = [square[0]*2, square[1]*2, extension(square[2]), square[3]*2]
fig = plt.figure()
fig = plt.figure(figsize=(10.0, 8.0))
Axes3D(fig)
for x, y, z, i in zip(cube[0], cube[1], cube[2], range(len(cube[0]))):
    plt.plot([x], [y], [z], marker="o", linestyle='None', label=str(i)+' : '+format(i, '0'+str(3)+'b'))
plt.legend()
plt.show()
```

![対応関係.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365961/3d8beaaa-1fb0-a46e-c4c9-9ef22904b77d.png)

　ここから平面は何個取れるだろう。取り方は、1つの次元を固定して、残りの次元で 00 -> 01 -> 10 -> 11 の変化になっている個数を数えればよい。先ほどと同じように色分けしてもよいが、実は1次元目を 0 or 1 に固定して得られるのは2個であり、2次元目を固定しても2個、3次元目を固定しても2個である。つまり、平面が6個取れる。さらに言ってしまえば、何次元に拡張しても同じように取ることができ、つまりは N次元の超立方体 は N-1次元の超立方体 2N個 で構成されている。つまり、立方体から正八胞体に拡張すると、正八胞体は4次元の超立方体なので3次元の超立方体である立方体 2×4 = 8 個 で構成される。 可視化してもよいが、サイコロを展開しても分かるように、展開方法によって形状はまちまちになるためやめておこう（実際に展開した図はいっぱい転がってるしね）。

# まとめ

これでよくある正八胞体を展開すると8個の立方体になるというのが納得できるかと思う。まぁそんなに難しくないってことですね。