くろみーの日報テンプレ

日常のほのぼのした記録

Mac で X Window System を有効にする

最近家のコンセントの最適化をしたらプラグが一個空いたのでラズパイを常駐させて遊んでいます

ラズパイで何をするのかといえば、Linux の勉強です

この本の内容をぽちぽちと打っているのですが、進めていく中で大きな壁にぶち当たりました

「画像、どうやってみればいいんだろう」

チュートリアルの中で Python で画像をプロットして確認する箇所があるのですが、sshでログインしているだけだと画像を見ることができません

もちろん scp などで画像をダウンロードしてローカルで見ることはできますが、毎回画像をダウンロードするのもめんどくさい

ちょっと調べたところ便利なコマンドを見つけました

shuzo-kino.hateblo.jp

feh というコマンドを使うと ssh 接続でも画像が見れるみたいです

しかし早速やってみるとエラーになってしまいました

$ feh sched-1.jpg
feh ERROR: Can't open X display. It *is* running, yeah?

どうやら feh を動作させるにはクライアント側で X Window System を有効にする必要があるようです

X Window, 昔 Ubuntu を使っていた時にチラッと聞いたことがありましたが、Macではどうやるんだろうと思っていたら丁寧に説明してくれているブログを見つけました

itcweb.cc.affrc.go.jp

このブログの通りにやってみたところ、無事 xQuartz (Mac OS 版の X Window System 互換ソフト)がインストールできました

いざ、ssh 接続 忘れずに ssh -Y オプションをつけて接続しましょう

$ feh sched-1.jpg

無事画像を見ることができました

ラーメン界の Basic Object を食べに行った話

おはようございます

 

今回はラーメン界の Basic Object を食べに行った話をしようと思います

 

Basic Object というのはもちろん Ruby のアレのことです

docs.ruby-lang.org

 

Ruby では基本的に全てのクラスが Object クラスを継承していますが、その Object クラスのさらに親に当たるクラスがこの Basic Object です

 

ある秋の昼下がり、会社のラーメン愛好家三人で渋谷の有名なラーメン屋さんに行った帰り、私は同じくラーメン好きの同僚からこのような指摘を受けました

 

??? 「くろみーさん、大勝軒を知らないなんて Basic Object を知らずに Ruby 使ってるようなもんですよ」

 

どうもS氏によれば現在主流となっている東京のラーメンはほとんどこの大勝軒から派生したラーメンであるとのことでした

 

たしかに、Basic Object を知らずに Ruby を使うのはいかにもにわか Rubyist

 

つまり大勝軒を知らずにラーメンを語るのも等しくにわかというわけでしょう

 

というわけで行ってきました

 

場所は永福町駅から徒歩1分のところ

 

10分ほど並んでカウンターに案内されました

 

メニューはシンプルで基本の中華麺とチャーシュー麺のみ

 

初回ということもあり、私は中華麺と生卵のセットを注文しました

 

届いたのがこちら

 

 

見た目はオーソドックスな醤油ラーメンですが、スープを一口飲んで驚きました

 

濃厚な鰹の香りとコク、そして何よりスープが熱々なのです

 

そんなの当たり前じゃないかと思われる方もいるかもしれませんが、このスープ、食べ終わるまで10分以上熱々を保っていました

 

最初なんで普通の醤油ラーメンに生卵がセットであるんだろうと思ったのですが、謎が解けました

 

生卵はこの熱いスープを中和するための緩衝材でもあったのです

 

ラーメンは並盛りでかなりのボリュームがありますが、その美味しさも相まってあっという間に完食

 

その間、店員さんが常に気を配ってお冷を切らさないように補充してくれたのも Good でした

 

大勝軒、一見普通の醤油ラーメンに見えますが、実態は非常に個性的でここでしか食べれない唯一無二の味とサービスを提供してくれました

 

ラーメン界の Basic Object なんてどうせありきたりな当たり障りのないラーメンが出てくるだけだろう

 

そう思った私をコテンパンに打ちのめしてくれた至極の一杯でした

Rで JINS MEME のデータを分析する

前提

JINS-MEME という製品があります

jinsmeme.com

かけているだけで生体データを取得して活動量や精神状態などを分析してくれるすぐれものです

ダッシュボードを備えたスマホアプリが公式で提供されていますが、実は JINS-MEME には Web API があってここから自分のデータをCSV形式で取得できます

jins-meme.github.io

三連休で暇なので Web API 経由で自分のデータを取得して R で分析してみました

チュートリアルをRでやってみる

公式から JINS-MEME のデータを使った分析例が提供されていますが、これは Python で書かれているので R でやり直してみます

今回試してみたのは頭部運動回数を週ごとに分析した例で公式のドキュメント を参考に試行錯誤して R で実装してみました

まずはデータの取得からですが、私の場合自作のパッケージを使って取得したデータを Bigquery に上げているのでここから取得するようにしています

query <- "SELECT * FROM `my-health-dataset.jins_meme.60s_interval_data`"
tb <- bq_project_query(project, query)
df <- bq_table_download(tb)
df <- as_tibble(df)

ダウンロードした後は文字列型で保存されているタイムスタンプを日付型に変換し、そこから week number を抜き出します

R には lubridate::week という便利な関数があるのでこの処理は1行で書くことができます

df <- df %>%
  mutate(
    timestamp = as.Date(date),
    week = week(timestamp)
    )

続いてチュートリアルでもあったようにデータのフィルタリングを行います

# filter dataframe
df <- df %>%
  filter(wea_s >= 20, tl_yav >= -45, tl_yav <= 90, tl_xav >= -45, tl_xav <= 45)

まずは週ごとの累積密度分布を書いてみました

# plot density histogram of hm_yo by week
df %>%
  ggplot(aes(x = hm_yo, color=as.factor(week))) +
  stat_ecdf(geom = "step") +
  scale_x_log10() + 
  labs(title = "Cumulative Histogram of Head Movement (Yaw) by Week",
       x = "headMoveHorizontalCount (log)",
       y = "density")

これだけだとなんじゃこれという感じですが、対数プロットで直線になっているので、見る人が見ると頭部回転運動の回数は冪乗分布であることがわかります

なのでチュートリアルにもあるように、この場合は平均を取る代わりに 90 パーセンタイル点をとって、週ごとに比較してみます

df %>%
  group_by(week) %>%
  summarise(
    hm_yo_90 = quantile(hm_yo, 0.9)
    ) %>%
  ggplot(aes(x = week, y = hm_yo_90)) +
  geom_line() +
  labs(title = "90% Quantile of Head Movement (Yaw) by Week",
       x = "week",
       y = "headMoveHorizontalCount")

僕一人のデータだけだと特に何がわかるというわけでもないんですけど、チュートリアルにもあるように、他の人のデータと比べるとその人の活動量の大きさがわかるかもしれません

集中度の分析

チュートリアルで分析した頭部回転運動は指標として少し味気ないので、次に多少意味のある指標として JINS MEME から得られる「集中度」を時間ごとに分析してみたいと思います

公式のドキュメントによると「集中度」というのは

まばたき間隔平均値を利用し「ある一つのタスクへの注意が続いている状態」を指標化しています

とのことでおそらく、まばたき間隔が長くなると集中度が高いと判定されるものだと思われます

スコアは0-100の間で表されスコアが高いほど集中度が高いと判定されたことを意味します

集中度のデータは15秒ごとのデータになるので今回は 15s_interval_data というテーブルを読み込みます

また今回は時間ごとに分析したかったので week のほかに hour も変数に追加します

# set up query
query <- "SELECT * FROM `my-health-dataset.jins_meme.15s_interval_data`"
tb <- bq_project_query(project, query)
df <- bq_table_download(tb)
df <- as_tibble(df)

# assign hourly number to each row
df <- df %>%
  mutate(
    timestamp = as_datetime(date),
    hour = hour(timestamp) + 9,
    week = week(timestamp)
    )

仕事中の集中度を比較したいので、勤務時間中のログのみを吸い出します

df.work = df %>%
  filter(vld == TRUE, hour >= 10, hour <= 19, tl_yav >= -45, tl_yav <= 90, tl_xav >= -45, tl_xav <= 45)

hour, week ごとに集中度の 50 パーセンタイル点を抜き出す

# extract 50 % quantile of sc_fcs by hour and week
df.work.summary = df.work %>%
  group_by(hour, week) %>%
  summarise(
    sc_fcs_50 = quantile(sc_fcs, 0.5)
    )

いざ、プロット

df.work.summary %>%  
  ggplot(aes(x = hour, y = sc_fcs_50, color=as.factor(week))) +
  geom_smooth(se=FALSE) + 
  scale_x_continuous(breaks = seq(10, 19, 1)) +
  labs(title = "50% Quantile of Focus Score by Hour",
       x = "hour",
       y = "Focus Score")

結構面白い図が書けました。集中度には2つ山があって、12-13時と16-17時で高くなっていそうな感じです

せっかくなのでもうちょっと見やすいように週ごとの曲線をまとめてみます

df.work.summary %>%
  ggplot(aes(x = hour, y = sc_fcs_50)) +
  geom_point() +
  geom_smooth() +
  scale_x_continuous(breaks = seq(10, 19, 1)) +
  labs(title = "Boxplot of Focus Score by Hour",
       x = "hour",
       y = "Focus Score")

先ほどの図の考察で2つ山があると言いましたが、一方で大まかな傾向としては午前中の方が午後よりも集中力が高いようです

私は以前朝方夜型診断というものをやったことがあって、結果は中間型(偏差値50)だったのでエンジニア界隈では珍しく?午前中が一番捗るタイプのようです

感想

今回は R で JINS MEME のデータを使って遊んでみました

メガネ型のウェアラブルバイスとしてはかなり先駆的な製品なのでセンサーの精度など正直あまり期待してなかったのですが、自分でも結構納得の結果が出て驚いています

自分が朝方か夜型か知りたい人は是非ともかけてみてください

この分析を通してメガネ愛が強まった人にはこちらの商品もおすすめです

suzuri.jp

Fitbit を使った健康報告パッチを Github Action に対応させた話

はじめに

健康はあらゆる物事の前提

というわけで、最近 Fitbit を使って自分の健康管理をしているのですが、最近こんなツールを見つけました

github.com

概要欄にも書かれているとおり

毎晩23時にFitbit APIを使って健康情報をTwitterに投稿する。プログラムの実行は、サーバのcronで登録する。

というアプリです

非常に魅力的なのですが、自前のサーバーを持っていない私としてはこれが Github Action でやれたら最高、と思っていたので自分でこのアプリを拡張して Github Action で自動的にその日の健康情報サマリーをTwitterに投稿してくれるアプリを作りました

こちらです

github.com

拡張したと言ってもやったことはシンプルで

  1. Github Actions のワークフローを作成
  2. Twitter API 連携のキーを環境変数
  3. Fitbit API 接続キーをGoogle Cloud Storage で管理する

の3つです

作成したワークフロー

以下のような yml ファイルを作成してワークフローを定義しました

name: Post Fitbit summary to Twitter
on:
  schedule:
   - cron: '5 14 * * 1-5' # 23:05 JST

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Set up Python 3.8
        uses: actions/setup-python@v2
        with:
          python-version: 3.8
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install matplotlib
          pip install tweepy
          pip install google-cloud-storage
      - name: Post summary to Twitter
        run: |
          echo -n '${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}' > key.json
          python main.py
        env: 
          GOOGLE_APPLICATION_CREDENTIALS: key.json
          TW_CONSUMER_KEY: ${{ secrets.TW_CONSUMER_KEY }}
          TW_CONSUMER_SECRET: ${{ secrets.TW_CONSUMER_SECRET }}
          TW_ACCESS_TOKEN: ${{ secrets.TW_ACCESS_TOKEN }}
          TW_ACCESS_TOKEN_SECRET: ${{ secrets.TW_ACCESS_TOKEN_SECRET }}

元のスクリプトと同様に python main.py で実行する部分は同じですが、Github Actions に対応するために Twitter API の認証情報を環境変数化しました

元のスクリプトだと twitter_conf.json というファイルに認証情報を記載する設定になっていますが、Github Actions を使う場合、レポジトリ内のファイルに認証情報を記載することになり危険です

このように環境変数にすることで安全にAPIの認証情報を格納することができます

一方で難しかったのが Fitbit APIの認証キーの取り扱いです

Fitbit API の認証キーは有効期間が24時間と短く、期限が切れたらその都度 refresh token を使って新しい認証キーを取得し直す必要があります

これをActions の環境変数に設定した場合、Github の PAT (Personal Access Token) を作成して Github API を通して環境変数を更新する処理を入れ込む必要があるので面倒です

そこで今回はクラウドストレージを活用して認証キーの管理を行うことにしました

AWSのS3と迷ったのですが、Google 繋がりで Google Cloud Storage を採用しています

具体的には、元のスクリプトでファイルの書き込みを行う部分をこのように書き換えるだけです

    blob.upload_from_string(
        data=json.dumps(conf),
        content_type='application/json',
    )

この方法であればGoogle Cloud Storage の認証キーを一度入れるだけでFitbit APIの認証キーの更新を自動化できるので便利です

できたもの

作成したワークフローで投稿されたTweet はこちらになります

みなさんも Fitbit API を活用して健康を高めていきましょう

俺の最強の論文執筆環境

今私が使っているTeXでの論文執筆環境

色々試してみたけど結局これに落ち着いている

 

これまでは主に有料版の Overleaf と Paperpile を使っていたのだが、就職をきっかけにできるだけ無料でできる範囲でやりくりしてみようと思って試行錯誤した結果、上のような構成になった

 

しかし体感としてはむしろ有料のサービスを使っていた時よりも(趣味の)論文執筆が捗っている気がする

 

特筆すべきは VSCode と Cloudlatex の相性の良さで、Cloudlatex の VSCode プラグインを使うことで Git で原稿を管理しつつ同時にコンパイルを行うことができる

 

コンパイルクラウド上で行われるのでローカルマシンにTeXの環境構築をする必要もないし、気が向いたら外出先からも原稿の執筆ができるので便利

 

Cloudlatex は Overleaf に比べるとあまり知名度はないかもしれないが、Overleaf で github 連携をしようとすると有料版を契約する必要があるので、無料でこれだけできるサービスはかなり重宝している

 

ZoteroDropbox の相性の良さもこれまで気づかなかったところで、Zotero はデフォルトだと無料で500MBまでしか論文を保存できないのだが、ファイルのアップロード先として Dropbox を使うと Dropbox の無料枠である 2GB まで論文を保存することができる

 

Paperpile だとストレージは個人の Google Drive が使われるのでメールや写真と競合して個人用のアカウントの容量を食ってしまうのが苦手だった

 

幸いにも Dropbox は今まで使ったことがなかったので、丸々論文用のストレージとして使えるのも計算がしやすくていい

 

設定の参考になる記事を以下にメモしておく

note.com

github.com

BibTeX と BibLaTeX の違いでハマる

ご縁があってなんだかんだ大学を離れた後も TeX を触る機会が多い

学部・修士・博士と合わせて10年以上大学に所属していた身としては LaTeX の扱いももう慣れたもの・・・

と思っていたのだが、最近まで TeX の文献管理ツールに BibTeX と BibLaTeX の2つがあることに気づかなかった

 

どうやら BibTeX の新バージョン(互換性はない)として BibLaTeX というのがあるらしくて、今日 Zotero を使って文献情報をエクスポートしたら、知らない間に BibLaTeX でエクスポートされていて、コンパイルエラーで大変な目にあった

 

そもそも LaTeX のエラーの内容がいちいちわかりにくいという愚痴は置いておいて、BibTeX と BibLaTeX に互換性がないのはどうにかならないだろうか

 

せっかくなので両者の違いについて我が家の ChatGPT くんに聞いてみた

BibTeX: BibTeXのデータモデルは比較的単純で、文献タイプ(例: book、article、inproceedings)とそのフィールド(例: author、title、year)から構成されます。
BibLaTeX: BibLaTeXはより豊富なデータモデルを提供し、文献情報を詳細に表現できます。例えば、DOI、ISBN、URLなどの情報をより柔軟に扱えます。

DOI とか URL は最近の文献であればほぼ必ずついてくる情報なので、BibLaTeX であればこれがデフォルトで扱えるようになるのはありがたい

 

詳しくは BibLaTeX の公式ページを参照されたし

CTAN: Package BibLaTeX

 

ただし Zotero も工夫次第では BibTeX 形式でのエクスポートができるらしく、以下の記事が参考になりそうだった

 

zenn.dev

 

TeX は自分で一から書くというよりは配られたテンプレートを使うことの方が多いから、まだ BibTeX のままで止まっている雑誌も多い

 

たぶんよほどのことがない限りは、自分はこれからも BibTeX を使い続ける気がするが、TeX の世界も色々とアップデートされていってるんだなぁと思った昨今

Stub と Mock 完全に理解した

Shun57 さんのこのブログ記事を読んで n 年ぶりに Stub と Mock を完全に理解した

zenn.dev

Stub は依存コンポーネントを都合のいい値を返すように置き換えたものであり、Mockは依存コンポーネントが正しく呼び出されているかチェックするためのものである

 

つまり、テストの目的が依存コンポーネントが正しく呼ばれるかだったら Mock を使うし、依存コンポーネントから何らかの結果を受け取ってその結果を正しく処理したことを確かめたいのであれば Stub を使う

 

今は完全に理解しているけど1ヶ月経ったら完全に忘れてる説が濃厚

 

その時のための備忘録としてこの記事を残しておく