HPCメモ

HPC(High Performance Computing)に関連したりしなかったりすることのメモ書き

外音取り込みイヤフォン&ネックスピーカーレビュー

アイワの肩乗せスピーカーButterflyAudioと、Sonyの穴空きイヤフォンLinkBudsを発売直後に買いました。 3ヶ月近く使ってきて、私の中での評価が定まってきたので、これまで使ってきた外音取り込み系イヤフォン&ネックスピーカーと合わせてレビューします。

まずお約束

  • 全て私が購入して使用した製品のレビューです。内容は主観的なものなので私個人の好みや体格なども影響しているかもしれません。
  • あしかけ4年ほどかけて購入&使用してきたた製品なので、既に生産終了されているものもあります。
  • ページ内のamazonへのリンクにはアフィリエイトが含まれています。

レビュー対象(購入順に紹介していきます)

  1. ソニー ワイヤレスノイズキャンセリングヘッドホン WH-1000XM2
  2. ambie wireless earcuffs
  3. earsopen PEACE TW-1
  4. JVC SP-A7WT-B NAGARAKU ウェアラブルネックスピーカー
  5. aftershokz(現 shokz) Opencomm
  6. アイワ ButterflyAudio HPB-SW40
  7. ソニー LinkBuds

ソニー ワイヤレスノイズキャンセリングヘッドホン WH-1000XM2

good

bad

  • マイクが貧弱
  • 外音取り込み機能が使いづらい

私が最初に使った外音取り込み系のデバイスソニーのワイヤレス密閉型ヘッドホンWH-1000XM2でした。

これを外音取り込み系と言っていいかどうか微妙なとこですが、ヘッドホンの耳の部分に手をあてるとマイク(たぶんNC用のもの)で取り込んだ音をそのまま再生してくれるという機能があります。ただし、新幹線や飛行機で移動中にこのヘッドフォンを使っていると、車内販売の人やCAさんが目の前に来た時に耳に手を合てて外音取り込みモードにしてても「音楽聴いてるっぽいから邪魔しないでおこう」って感じでスルーされるのでこの機能が役に立った記憶はないですw

そんなわけで、このヘッドフォンは外音取り込みヘッドフォンとしての評価はほぼ0点なんですが、個人的には外音取り込み機能の重要性を教えてくれた機種です。

なんと恐しいことに、


夜中にこれつけてYouTubeを見てたら、ベットから落ちて大泣きしてる子どもを抱えた奥様が背後に立ってました・・・o......rz


以来、本当に集中して良い時以外は常に外音取り込み系のヘッドフォン/イヤフォンを使うようになったのでした :p

一応、amazonではこのモデルもまだ買えるようですが、最新の後継機種はこちらです。

covid19のせいで出張の機会が激減し売却してしまいましたが、NCヘッドフォンとしては本当に優秀なので、もし以前のようなペースで出張するようになったら後継機を買おうかと思っています。

ambie wireless earcuffs

ambie.co.jp

good

  • 装着しててもほとんど目立たない

bad

  • 先端のシリコンパーツが良く落ちる
  • 耳に挟む部分が固くて痛い

さて、私が初めて使った、まともな外音取り込み系のイヤフォンはambieのearcuffsです。 残念ながら既に生産終了になってますが、完全ワイヤレスの後継モデルが昨年の夏から販売中のようです。

この機種は超小型のスピーカーを耳に挟んで、耳の穴に向けて指向性を絞って音を送るような形になってます この構造のおかげか、ちょっと大きいイヤフォンくらいのサイズ感で、耳の外につけてる割には存在感がほとんど無いのが良い点でした。

公式サイトには装着して外を走っているイメージ写真が載っていたので、真似してジムで走る時につけて音楽を聴いてたこともあるんですが、ボリューム最大にしても音量が小さめなのでまわりがうるさいとほとんど聴こえなくなります。 また、先端部分のシリコンパーツが取れやすく何度か紛失してしまいました*1

web会議の時にも使ってましたが、耳に挟む部分が狭くて固く1時間も使っていると耳が痛くなってきたのもちょっと辛かったです。

結局、数ヶ月使ってましたが「外音取り込みイヤフォンは良いんだが、こいつとは相性が悪い」という結論になって、次のearsopenが到着した時点で処分してしまいました。

earsopen PEACE TW-1

good

  • 左右独立の充電ランプ
  • (ambieと比較すると)柔らかくて耳にダメージを与えない付けごこち

    bad

  • ぱっと見で分かるくらい目立つボディサイズ
  • バンド部分が折れた

前のambieを使っている頃に、もっと良いのが無いかなぁと探していた時にこれのクラファンをみかけて購入(?)しました。

earsopenもambieと同じように耳に挟むスタイルなんですが、つけごこちは比べものにならないほど柔らかかったです。 さらに、こちらは骨伝導式で位置が多少ずれてても同じように聴こえるので、長時間使う時はつける場所をずらしながら使うという手も使えました。

あと、地味な点ですが、この機種の充電ランプは左右独立していて、個別に充電中かどうかが分かるようになっています。 ケースに戻して充電ランプもついてるので大丈夫と思っていたら片方しか充電できておらず、いざ装着するとバッテリーが切れているという悲しい事故を防げる大変素晴しい機能なんですが、この機種以外では採用されているのを見たことが無いですね。

この機種最大の弱点は、おそらく見た目です。webサイトの写真を見てもらえば分かりますが、ものすごい存在感です。これをつけて仕事してて家族に「何つけとんかと思った」と言われたことは一度や二度ではありません。あと、骨伝導方式のためなのか結構重たくて、つけたまま左右に顔を振ると、耳だけ振られるような感覚がありました。

もう一点、残念なことに耳を挟む部分の柔らかさが災いしたのか、数ヶ月程度の使用で折れてしまいました。*2*3 折れたのに気付いた時は既に次の次に控えているOpencommのクラファンに申し込んでいたので、修理するという発想もなくそのまま廃棄してしまいましたが、もうちょっとサイズが小さければ修理して使い続けていたかもしれません。

そして、そんな後悔とともに生きている私のために後継機種のクラファンが絶賛実施中です。

greenfunding.jp

ぱっと見たところ、前機種の不満がほぼ全て解消されているようなので、とりあえず申し込んでいます。 届いてからのお楽しみですが、出来栄えが良ければ何か書くかもしれません。

JVC SP-A7WT-B NAGARAKU ウェアラブルネックスピーカー

good

  • 軽い

bad

  • 音だだ漏れ

earsopenが死ぬ前後に、ちょっと発想を変えてネックスピーカーを試してみようと思って買ってみたんですが、これは大失敗でした。 自分が聴きやすい音量に設定すると、隣の部屋に居る家族から「何か音がしよるよ?」と言われるような感じでした。

唯一の利点はもの凄く軽いってことで、朝起きてなんか首に違和感があるなぁと思ったらつけっぱなしだったなんてこともあったくらいです。

aftershokz(現 shokz) Opencomm

good

  • 常用できるレベルのマイク
  • 骨伝導とは思えないクリアな音質

bad

  • ネックバンドが固い
  • 持ち運び時のサイズが大きい

ネックスピーカーが期待に反してまるで使いものにならなかったので、しばらくは折れたearsopenをだましだまし使っていたのですが、Opencommを使い始めた瞬間に、ネックスピーカーもearsopenも処分してしまいました。当時は、もうこれ以上は外音取り込み系のは買う必要が無いかと思ってたんですが、そう簡単に沼から抜け出せるわけが無いですね o...rz

Opencommはネックバンド型のイヤフォンですが、耳に入れるのではなく骨伝導式のユニットを耳のちょっと前のところに当てて使います。ブームマイクもついていてしっかり口元で音を拾える形状になっています。 今まで紹介してきた機種ではほとんどマイクが使いものにならなくて、web会議の時は別にマイクを用意してしゃべってましたが、このマイクは優秀です。自分が話しているところを録音したものを聴いてみましたが、クリアに聴こえるし環境音もほとんど拾ってませんでした。

また再生音質も骨伝導式とは思えないくらいクリアに聴こえて、会議などの話し声だけではなく、音楽もそれなりに聴けるレベルです。*4

弱点は、ほぼ物理形状に起因する問題だけなんですが、意外と使い勝手に影響します。まずネックバンド部分にチタンが入っていて見た目よりそうとう固いです。しかもサイズが調整できないので、装着すると後頭部から1,2cm後ろに浮いた状態で使うことになります。この状態で、リラックスして音楽を聴こうと椅子にもたれかかると・・・ヘッドレストに押されてずれますw

また、ネックバンド部が細いこともあって、外出先で使う時などの持ち運びには気を使います。付属品のセミハードケースに入れて持ち運んでましたが、このケースが大きくて結構場所を取ります。防御力は高そうで良いんですけどね。

アイワ ButterflyAudio HPB-SW40

aiwa.base.shop

good

  • サラウンドスピーカーのような聴き心地

    bad

  • 重い
  • 首を振った時に音像に違和感が生じる

ButterflyAuddioは一応ネックスピーカーに分類されるものなんですが、普通のネックスピーカーはスピーカーを肩に載せた状態で使うのに対して、こちらは肩に載った土台からスピーカーが生えていて耳のすぐ前に設置されるような形になっています。 ヘッドフォンを手でもってちょっと耳から離して聴いてるような状態で、独特な聴きごこちを感じられます。

音質は別格で、この記事に上げている他の機種では比べものになりません。(というか、うちにあるスピーカーも含めた全てのデバイスの中で一番良いです)

上には弱点として「重い」と書きましたが、正確には重量バランスが悪いと言うべきかもしれません。重心が上寄りな上に首にかける土台部分が円柱状になっているため、ちょっと体を揺らすと本体が揺れて実際の重さ以上に重みを感じます。 あと、耳とスピーカー部の距離がシビアなので、首を傾けただけで左右のバランスが崩れます。映画とかを見てる時は特に気になりませんが、音楽を聴いてる時は違和感が大きいです。でも、実際のところこれを使う時は(重量バランスの問題もあって)椅子にがっつりともたれているので慣れてきた今となっては大きな問題ではなくなってきました。

ソニー LinkBuds

good

  • 自然な外音
  • 音質が良い

bad

  • スピークtoチャットを有効にすると、独り言に反応して再生が止まる
  • バッテリーが持たない

最後に紹介するのはSonyのLinkBudsです。一般的なイヤフォンみたいなサイズですが、耳に入れる部分がドーナツ状になっていて、耳に蓋をするような形で装着すると中心部の穴を通って外部の音が聴こえてくるという仕組みです。聴こえ方はambieと似たような感じで再生される音と外音が自然にミックスされて聴こえてくるので、これで何か聴きながら目の前の人と会話すると「自分が聴いているものが相手に聴こえていない」ということに違和感を覚えるくらい自然に聴こえてきます。

今のところ、これをメインで使っていますが、装着時の違和感が少なく、長時間使っていてもほとんど痛くなりません。*5

マイクも、耳のそばで集音しているわりには明瞭で、これまでweb会議等で使ってきましたが相手から聞き返されるようなこともなく問題無く使えています。

さらに、360 reality audioにも対応しているので、アプリとスマホなどを設定するとなかなか面白いサラウンド体験ができます。 対応曲の中でもサラウンド感が強いものや弱いものがあるので当たりを引ければ楽しめそうです。*6

イヤフォンとしての性能は非常に優秀なんですが、残念ながらバッテリーの持ちが悪くて3〜4時間しか持たないので、 終日の会議に参加する時なんかは注意が必要です。充電ケースだけで2、3回はフルに充電できるはずなので片耳づつ充電しながら使えれば問題無かったんですが、残念ながら右耳を充電すると電源がOFFになるのでこの手は使えませんでした。

あと、スピークtoチャットという自分が話し始めたら一時停止してくれる機能がついているのですが、これが独り言にも反応してしまうので私の場合は使いものになりませんでしたw

まとめ

音質やマイク品質だけを考えると、LinkBudsで全てをまかなえそうなデキなんですが、これ一本に絞るためには最低2倍、できれば3倍はバッテリーが持たないと厳しいところです。仕方がないので、今のところはLinkBudsをメインに使いつつ長時間の会議の時はOpencommを、音楽を聴いたり動画を見て遊んでる時はButterflyAudioを使うという形で使いわけています。

この記事を書いてるうちに、LinkBudsをもう1台買って、交互に充電しながら使うのが私には一番良さそうな気がしてきてますが、earsopenの後継機種をクラファンで支援しているので、これが届いてからまた考えるつもりです。

*1:ただし公式サイトで数百円で売ってたのであまりダメージは無いです。

*2:クラファンのリワードでいただいた初回ロットに近いものなので、一般販売時には改善されていたかもしれません。

*3:あと、この記事を書きながら検索してたら、なんか折れたけど無償で修理してもらえたという情報もちらほらとありました・・・orz

*4:といいつつ、この後の2機種と聴き比べたらかなり落ちますが

*5:とはいえ、バッテリーがあまり持たないのでせいぜい3〜4時間しか連続で使ったことは無いですが

*6:ちなみにオーケストラ曲はほぼ外れです。楽器毎の定位がはっきりしすぎて気持ち悪いので

PyVistaでjupyter notebookに3D画像を表示する

この記事は Calendar for UdonTech Advent Calendar 2021 | Advent Calendar 2021 - Qiita の3日目の穴埋め記事です。

pyvistaとは

pyvistaとはpythonでVTKを扱うためのモジュールです。 "VTK for humans" という素晴らしい響きのキャッチフレーズが全てを表わしているんじゃないでしょうか。

docs.pyvista.org

VTKというのは、数値計算業界ではデファクトスタンダードとなっている*1可視化用のファイルフォーマットと、それを扱うためのToolkitです。たぶんComputer Visionの人達には、ポイントクラウドレンダリングに使ってるやつとして親しまれてるんじゃないでしょうか。*2

VTKの可視化の例を知りたい方は、こちらのギャラリーをご覧ください。

vtk.org

pyvistaを使うとこれが人間にも簡単に扱えるということに加えて、jupyter notebookでのレンダリングもできるということのようなので、今日はこれを試してみたいと思います。

Jupyter Notebook Plotting — PyVista 0.32.0 documentation

テスト環境

  • MacBook Air (M1, 2020)
  • macOS Monterey 12.1
  • jupyter lab desktop app version 3.2.5-2
  • conda 4.11.0

インストール

condaでpyvista本体をインストールします。

conda install -c conda-forge pyvista

続けてjupyterから使うためのモジュールがいくつか用意されているので、とりあえずかたっぱしから入れてみます。ただし、ipyganyについては

Currently, this backend has inferior support and features than the pythreejs.

とのことなのパスで。

また、itkwidgetsも

JupyterLab 3 support is not yet available. JupyterLab 2 or the Jupyter Notebook are possible alternatives.

とのことなのでパスしました。

pythreejs

conda install -c conda-forge pythreejs

panel

conda install -c conda-forge panel 

ipyvtklink

conda install -c conda-forge ipyvtklink

この3つはどう違うかというと

pythreejs - three.jsを使ってクライアントサイドでレンダリング

panel - vtk.jsを使ってクライアントサイドでレンダリング

ipyvtklink - サーバサイドのPyVistaを使ったレンダリング結果をノートブックへストリーミング

ということのようです。

インタラクティブにやるなら、pythreejs, 重いデータでjupyter labのサーバとクアイアントの距離が離れてる時(colabを使う時とか?)は、ipyvtklinkの方が有利になりそうですね。

まーぶっちゃけうちの環境(jupyter lab desktop app)なら、サーバサイドかクライアントサイドかの違いは大差無いはずなので、レンダリングエンジン(threejs, vtk.js or vtk)の違いだけですね。

バックエンドの切り替え方法

Jupyter Notebook Plotting — PyVista 0.32.0 documentation

set_jupyter_backend()を使って、事前に設定しておくか、plot時にjupyter_backend引数から渡せば良いみたいです。

実際に表示してみる。

pythreejsのサンプルとして載っているこちらのコードを使って、jupyter_backend引数を変えて各レンダリングエンジンで同じものを表示してみました。

import pyvista as pv
from pyvista import examples
# download an example and display it using physically based rendering.
mesh = examples.download_lucy()
mesh.plot(color='lightgrey', pbr=True, metallic=0.2,
          jupyter_backend='pythreejs')

pythreejs f:id:n_so5:20211228082644p:plain

panel *3 f:id:n_so5:20211228082658p:plain

ipyvtklink f:id:n_so5:20211228082713p:plain

画像のサイズがバラバラですが

  • pythreejs -> コード部まで含めて画面の高さに合わせる
  • panel -> 画面の幅に合わせる。高さはモデルのサイズに合わせる?(固定値かも)
  • ipyvtklink -> 表示部と画面の高さを合わせる

という挙動のようにみえます。

まず、操作性に関してですが、pythreejs、panelと比べるとipyvtklinkは格段に落ちます。 まさにparaviewなりfieldviewなりの可視化ソフトでリモート可視化をしている時の感覚に近いです。 データもレンダリングもローカルマシンでやっててこの操作感はちょっと辛い

表示品質は

  • pythreejs -> "キレイ"すぎて使えない
  • panel -> ごく普通(当然のことだけどparaviewと同じレベル)

といった感じです。

オプションをいじれば、また違ってくるとは思うんですが、なんかpythreejsの陰影の付け方が見映え重視のようで、これに物理量の表示を重ねたりするとちょっと見辛そうな感じがします。

ローカルにあるファイルの表示

前の例では、PyVistaが用意しているサンプルファイルをダウンロードしてきて表示しましたが、PC内にあるファイルを表示するケースの方が多いんじゃないでしょうか。

とりあえず、HPC業界内の超有名モデルをダウンロードして、表示してみました。

develop.openfoam.com

f:id:n_so5:20211228231056p:plain

彼はobjファイルとして配布されていますが、PyVista.read()で簡単に読めます!! 一応コードも書いておきますが、たったの3行です。

import pyvista as pv
mesh=pv.read('motorBike.obj')
mesh.plot(color='lightgrey', jupyter_backend='panel')

続いて、STLファイルが読めるか確認してみましょう。 データは国土地理院が配布している、香川県STLファイルを使います。

立体地図(触る地図サンプル・県別の立体模型)

こちらのページからSTLファイルをダウンロードして表示します。

f:id:n_so5:20211229003813p:plain

なんか凄い向きなんで、調整してみましょう。

mesh.rotate_x(-45)
mesh.rotate_y(180)
mesh.rotate_z(-45)
mesh.plot(color='lightgrey', jupyter_backend='panel')

f:id:n_so5:20211229005831p:plain

ドキュメントによると、STLとOBJ以外にも、BYUファイルというのと(当然ながら)VTKファイルも扱えるようです。

docs.pyvista.org

シミュレーションの実行結果ファイルがあれば、スライスを切ってコンターを描いたりなんて処理もjupyter lab上でできるようなので、レポート作成が捗りそうですね。

まとめ

Jupyterlabとpandas, matplotlib, PyVistaがあればたぶん私の仕事の報告資料全部作れる予感・・・

*1:個人の見解です

*2:という話を10年くらい前のうろ覚えの知識で書いてからぐぐったらVTK遅すぎるから他のに変えようぜ的な話がみつかりましたが・・・

*3:オプションにpbr=True, metallic=0.2って入ってますがpythreejsのオプションを消し忘れただけで、pnelでは無視されます。

fire stick TVでPCレスプレゼンテーション

この記事は Calendar for UdonTech Advent Calendar 2021 | Advent Calendar 2021 - Qiita の23日目の記事です。

TL;DR

はじめに

アドベントカレンダーを書いてるんだから、あたりまえっちゃあたりまえですが、世間では忘年会シーズンまっただなかですね。 covid-19の影響でここ2年は相当少なくなっていると思いますが、やはりこの時期になると

「飲むなら持つな、持つなら飲むな」

という会社からの圧力が高まっているのではないでしょうか。

UdonTechでは1月に(covid-19の様子を見つつ)新年会+LT大会を行なう予定になってます。 今月は同じお店で忘年会だったんですが、聞けばお店の大画面ディスプレイ(TV?)にFire stick TVを挿す予定だとか。

Fire StickTVでプレゼンができれば、飲み会に重たいPCを持っていく必要も無いし、心置き無く飲めますね というわけで、今日は来たる新年会に備えてFireStickTVでプレゼンする方法について検討していきます。

機器設定

新規に購入した場合は別ですが、amazon prime会員で、元々使っていたデバイスを流用しようと思ってる人は、最初に新規プロファイルを作成しましょう。

環境設定の中に、「最近見たアイテムを同期」という項目があって、これをオフにすると閲覧履歴などは表示されなくなるので大丈夫・・・と言いたいところですが「次に観る」のリストや「あなたが興味のありそうな○○」というリストもあって、特に「次に観る」のリストにはウォッチリストに入れているものがずらずらっと出てきます。

FireStickTVはディスプレイなどは無いのでPCでプレゼンをする時のように、手元の画面で諸々の設定を終わらせてからプロジェクターへの出力をオンにするといった手が使えません。微妙な空気を避けるためにも必ず設定しておきましょう :p *1

続いて、FireStickTVからwebが見れるか確認しましょう。

FireStickTVのwebブラウザはFireタブレットと同じくsilkブラウザで、デフォルトではインストールされていないかもしれません。*2 ホーム画面に"internet"と書かれたそっけないパネルがあるので、1回これを選択してみてください。 インストールされていればwebブラウザが起動するし、未インストールであればインストール画面に誘導されます。

なお、googleスライドではなくパワポを使いたいという方は、最初に書いたようにOperaブラウザをインストールする必要があるので、この作業と1,2は飛ばして「3. boxでスライドを共有してプレゼン」から読み進めてください。

1. google スライドで作成したスライドをgoogleドライブで共有してFireStickTVで表示

スピーカーはgoogleスライドで資料を作成し、googleドライブ上でFireStickTVの所有者(以降は"ホスト"とします)と共有します。

共有は、ファイルを右クリックして「共有」を選びホストのメールアドレスを入力するだけです。(デフォルトでは権限が「編集者」になっているので、必要に応じて「閲覧者」か「閲覧者(コメント可)」に落としてください)

f:id:n_so5:20211221162451p:plain

ホストはwebブラウザから https://drive.google.com/drive/my-drive へアクセスして、メニューから「共有アイテム」を開きます。 一覧に、スピーカーから共有されたgoogleスライドのファイルが見えるのでこれをポイントしてリモコンの決定ボタン(?)を押すと、次のようにgoogleドキュメントに移動するか確認されるのでそのまま移動します。

Fire Stick TVをリモコンで操作していると、この手のダイアログが表示されたタイミングでポインタの表示が消え、ボタンの間でしか移動できなくなります。ポインタが無くなったらカーソルキーを適当に何回か押すとフォーカスが移動していくので、「このページから移動」が選択された状態で決定ボタンを押してください。

f:id:n_so5:20211221162958j:plain

ちなみに、初めてのデバイスからログインするとgoogleから通知が来ますが、私の場合はなぜか「Nexus 7 での新しいログイン」という通知が来ました。

2. パワポで作成したスライドをOneDriveで共有してFireStickTVで表示

google スライドで作成するのも良いんですが、パワポでやりたいという人も多いですよね。(特に以前作ったものを使いまわす時とか)

この場合はパワポをOneDriveに置いてOffice onlineで表示すれば大丈夫。しかもamazonアプリストアにはOneDriveもOfficeもあります!

https://www.amazon.co.jp/Microsoft-Corporation-OneDrive/dp/B00IEG0JOY/

https://www.amazon.co.jp/Microsoft-Corporation-Office-Word%E3%80%81Excel%E3%80%81PowerPoint-%E3%81%AA%E3%81%A9/dp/B08WC1B9D2/






そういう風に思っていた時期が私にもありましたorz

まず、アプリストアにあるOneDriveもOfficeもFireStickTVは動作対象外のようでダウンロードすらできません。

さらに、私のテスト環境(会社のoffice365アカウントから個人のMSアカウントへ共有)だと、共有する側ではOKと表示されても共有された側には何の通知も来ず、共有ファイルは見えないままでした。

というわけでこの方法はちょっと使えなさそうです。

同じドメイン内のアカウント同士とか、個人アカウント同士なら大丈夫なのかもしれませんが、そこまで検証する時間はありませんでした。実はOneDriveも問題無く使えるよって方は良ければコメントしてってください。

3. boxでスライドを共有してプレゼン

さて、大本命だったOneDriveが使えなくてどうしようかと思ってたんですが、最近仕事でBOXを使い始めたのでこれを試してみることにしました。

BOXもgoogle driveと同じようにスピーカー側はアップロードしたファイルをホストと共有するだけです。 リスト表示だとファイル名の行の右の方にポインタを持っていくと「共有」ボタンが表示されます。

f:id:n_so5:20211221230550p:plain

これをクリックすると、共有相手を入力するダイアログが表示されるのでホストのメールアドレスを指定して招待します。

f:id:n_so5:20211221230702p:plain

ホストがFireStickTVからBoxにアクセスすれば、共有されたパワポスライドをOffice onlineで開けるので、そちらでスライドショーができるはずです。 ところが、silkブラウザからアクセスするとパワポを開いてもプレビュー表示になり、1枚目のスライドしか見れません。

ファイル自体にはアクセスできているので、単純に別のブラウザを入れてみることにしました。

3.1 Fire Stick TVにOperaをインストール

2021年12月の時点で、FireStick TVにインストールできるブラウザはsilk以外にはamazonアプリストアにはありません。

aptoide TVというストアアプリをインストールして、そこからamazonアプリストアには置いていないアプリを入れるのが定番のようですが、そんなことしなくてもoperaなら公式サイトに"google play storeが無いデバイス用に"とAPKファイルを置いてくれてますので、これをインストールすることにします。

f:id:n_so5:20211223170143j:plain

が、その前にOperaはリモコンでは操作できないので、Bluetooth接続のマウスを設定します。

まず、FireStickTVの設定画面から「コントローラーとBluetoothバイス」を選択します。

f:id:n_so5:20211223231622j:plain

「その他のBluetoothバイス」->「Bluetoothバイスを追加」と進むといきなりペアリングが始まるので、Bluetoothマウスをペアリングモードにしてください。 無事に接続できたら、右下の方にメッセージが出ると思いますが、メニューやホーム画面ではポインタが表示されないので、ここではマウスの動作確認ができません。 メッセージを信用して次に進みましょう。 o...rz

続いて、Operaをインストールするためにアプリストア以外からのインストールを可能にする設定を行ないます。 設定画面から「マイFire TV」を選んでください。

f:id:n_so5:20211223170812j:plain

「開発者オプション」->「不明ソースからのアプリ」と進んで「オン」にしてください。

次に、Operaをダウンロードするためのアプリをamazonアプリストアからインストールします。

こちらのdownloaderというアプリがメジャーなようですが、apkファイルが扱えてwebにアクセスできるものなら何でも構いません。

https://www.amazon.co.jp/AFTVnews-com-Downloader/dp/B01N0BP507/

このアプリをインストールして起動すると、次のような画面が表示されます。

f:id:n_so5:20211223233110j:plain

黄色の枠の中に、operaのダウンロードページのURL (https://www.opera.com/ja/download)を入力して"Go"ボタンを押してください。

f:id:n_so5:20211223233758j:plain

downloder内のブラウザに遷移して、operaのダウンロードページが表示されます。

ここで接続したマウスをちょっと動かしてみると、リモコン用のポインタ(赤丸)とは別にマウス用のポインタ(青丸)が表示されるはずです。 Operaのインストールはマウス無しでもできるので、もし青丸が表示されないようだったら先に進んでインストールまで済ませてからもう一度マウスのセットアップを行なってください。

このままだと見辛いという人は、右上のハンバーガーメニューから、Fullscreen Modeに移行しましょう。

f:id:n_so5:20211223170050j:plain

ブラウザを下の方へスクロールしていって「Opera Browser for Android」の部分にある薄いグレーで書かれた"Download the app here"のリンクをクリックしてください。APKファイルのダウンロードが始まります。

f:id:n_so5:20211223170152j:plain

ダウンロードが終わると、次のようなダイアログが表示されるので、「Install」を選んでください。

f:id:n_so5:20211223170222j:plain

これでインストール作業は終了です。

3.2 Boxからパワポでスライドショー

Operaを立ち上げて、https://app.box.com/ へアクセスします。

boxはgoogle driveのように共有アイテム専用のフォルダ的なものは無く、トップレベルに表示されています。*3

リスト表示の場合、パワポファイルの右の方にポインタを持って行くと[・・・]ボタンが表示されるので、ここから「開く」-> 「PowerPoint Online」を選択してください。「Microsoft Powerpoint」というメニューも表示されていますが、こちらは使いません。

f:id:n_so5:20211224001552j:plain

別のタブでパワポが開きますが、画面が遷移した直後はボタンやメニューがグレーアウトしています。10秒くらい待たされることもありますが、しばらく待っていると使えるようになります。

準備ができたら、右上の方にあるスライドショーボタンか、スライドショーメニューからスライドショーを始めてください。

スライドの左下にポインタを持っていくと、コントロールが表示されるので、ここでスライドの移動、スライドショーの終了などの操作が行なえます。(次のスライドへ進む時はマウスのクリックでも大丈夫です)

3.3 google driveからBoxへgoogle スライドのファイルをコピー

ここまで終えると、googleスライド派のスピーカーはgoogleドライブから、パワポ派のスピーカーはboxからファイルをホストと共有してプレゼンできる状態になりました。

しかし、これだと当日のオペレーションがちょっと面倒ですね(ブラウザのタブをちょろっと移動するだけなんだけど、Fire stick TVで複数タブ開くと結構辛いので・・・)

Boxはgoogle workspaceとも連携しているので、Boxにgoogleスライドをアップロードしてこちらに統一しましょう。

まず、googleドライブのweb版にアクセスします。スライドがあったら右クリックしてダウンロードすると自動的にpptxファイルに変換されると思います。 この仕様のせいで、一旦ダウンロードしてBoxへ再アップロードという手は使えません。 *4

右の方に、マウスを乗せると「アドオンを取得」と表示される + ボタンがあるので、これをクリックします。

f:id:n_so5:20211224005617p:plain

google workspace marketplace が表示されるので"Box for Google Workspace"をインストールしてください。

f:id:n_so5:20211224004611p:plain

google driveの画面でBoxのアドオンを起動して「ログイン」をクリックし、Boxへログインしてください。

f:id:n_so5:20211224004801p:plain

無事にログインできると、アドオン用のスペースにBoxのミニ版が表示されるます。 左ペインに表示されているgoogleドライブのファイルを、ここへドラッグ&ドロップすると、Boxにgoogleスライド形式のままでコピーできます。

ドロップする毎にファイルの権限を要求されると思いますが、その都度承認してください。

3.4 Boxからgoogle slideを使ってスライドショー

Boxの画面からgoogle slideで開くを選ぶところまでは、パワポと同じ感じです。

f:id:n_so5:20211224005948j:plain

別タブでgoogleスライドを開こうとしますが、「アクセス権が必要です」と表示されて先に進めません。

f:id:n_so5:20211224010332j:plain

ログイン名が表示されますが、これが間違ってないか確認して、画面右上にあるOperaのメニューボタンをクリックし、「デスクトップサイト」のスイッチを有効にしてください。

f:id:n_so5:20211223192610j:plain

自動的にリロードされてスライドが表示されるので、画面右上に表示されている「スライドショー」ボタンをクリックしてスライドショーを始めてください。

まとめ

冒頭にも書きましたが

  • ファイル共有はBox
  • ブラウザはOpera
  • Bluetoothマウス必須

以上の3点をまもれば、Fire Stick TVでもなんとかなるでしょう。

おまけ

実はBoxはkeynoteにも対応していて、keynote online *5を使ってkeynoteのファイルを表示することもできます。

f:id:n_so5:20211224010919j:plain

ところが、これ「デスクトップサイト」を有効にすると表示ができず、無効にすると表示はできるんですが、1ページづつ送るという操作ができません。

アニメーションとかの効果も使えるかどうか分からないので、多少の制約があってもどうしてもkeynoteでしゃべりたいんだという人以外にはお勧めできません。

*1:かえって盛り上がるかもしれませんが :)

*2:なんか色々と変遷があったので世代によって違うはず

*3:たぶんフィルターで所有者を基準に絞り込めるはず

*4:ちなみに、クライアントアプリを使って、googleドライブにアクセスし、スライドのファイルをオフライン使用に設定してもBoxへのアップロード時に弾かれます。

*5:本当にそんな名前かどうかは知らない

vuetifyのv-data-tableの表示をtruncateさせたい

この記事は Calendar for UdonTech Advent Calendar 2021 | Advent Calendar 2021 - Qiita の15日目の記事です。

vuetifyというvue用のUIフレームワークがあって、ここ数年こればっかり使ってます。*1

さて、vuetifyの中には、v-data-tableという高機能なテーブルがあります。

vuetifyjs.com

このテーブルコンポーネント、ちょっと文字列が長すぎると適当に改行してくれるという便利な存在なんですが 改行されると同じ画面サイズでも表示される行数が変わるので、不都合が生じる時もあります。

たとえば、今年話題になったアップル謹製キラキラネームの表を作ると、3行目の日本語版だけ改行されてしまってちょっと収まりが悪いですね。

f:id:n_so5:20211215003135p:plain https://codepen.io/so5/full/yLzgKqN

ここで、登場するのがv-text-truncateというヘルパークラスです。 これを適用すると指定された幅を超えた時に hogehoge... みたいな感じで省略してくれます。

vuetifyjs.com

特定の列だけにclassを指定するためには、item.<列名> というスロットを使ってその列だけ自分で書き直した上でclassを指定すれば良いので、こんな感じのコードになります

 <v-data-table :headers="headers" :items="items">
   <template #item.value="props">
     <div class="text-truncate">
       {{ props.item.value }}
    </div>
  </template>
  </v-data-table>     

f:id:n_so5:20211215003245p:plain https://codepen.io/so5/full/KKXaoeK

・・・省略されてませんね。

実はこれ、v-datatableの方で何かイロイロとやってくれるらしく、列幅を広げて無理矢理全部表示されてしまいます。

解決策は意外と簡単で、省略したい列にmax-widthを指定すると、その幅を超えた時に省略して表示してくれます。

f:id:n_so5:20211215003314p:plain https://codepen.io/so5/full/PojQMEj

無事に「スーパーキラキラカラフルク...」になりました!!

まとめ

  • v-data-tableの特定の列をカスタマイズする時は、item.<name>というスロットを使う
  • テーブル内で指定した幅を超えた文字を省略表示するためには、text-truncateクラスを適用した上で、CSSでmax-widthを指定する

*1:むしろコレが無いとUIとか作れないくらいどっぷりと依存しています。

log4jsの使い方

この記事は Calendar for UdonTech Advent Calendar 2021 | Advent Calendar 2021 - Qiita の14日目の記事です。

最近log4jが大騒ぎになってますが、今日の記事ではlog4jのnode.js移植版である、log4jsについて解説します。

log4js-node.github.io

インストール方法

npm install log4js

使用方法と解説

公式サイトに載っている一番シンプルな使用例は次のとおり

var log4js = require("log4js");
var logger = log4js.getLogger();
logger.level = "debug"; // default level is OFF - which means no logs at all.
logger.debug("Some debug messages");

このうち、実際にログを行なっているのは4行目のlogger.debug()の呼出し部分です。

一応順番に解説しておくと、各行で行なっている処理は次のとおりです。

  • 1行目: log4jsのモジュール読み込み
  • 2行目: loggerオブジェクトの取得(シングルトンになってる)
  • 3行目: ログレベルをdebug以上に設定

3行目でログレベルを設定している部分にコメントが書かれていますが、デフォルトだとログレベルはOFF(何も出力しない)になっているので、この点だけは注意が必要です。

簡単な中身の解説

ログレベルという設定があることからも想像できるかもしれませんが、log4jsのログ出力はsyslog的にカテゴリー(syslogだとファシリティー)とレベルの2種類の分類が行なわれます。

この例の場合、デフォルトカテゴリーに対して、debugレベルでの出力を行なっています。

カテゴリーを指定するには、getLoggerの引数としてカテゴリ名を渡し、レベルを指定する時はログ関数の名前(ここではdebug)を使います。*1 *2

ログ関数が呼ばれると、loggingEventというオブジェクトが生成されて、Appenderに送られます。

AppenderはさらにLayoutを呼出して、loggingEventを整形し、最終的な出力処理を行ないます。

適当に図解してまとめるとこんな感じになってます。

f:id:n_so5:20211213224322p:plain

appender の設定

デフォルトではappenderとしてconsole出力を行なう stdout が指定されています。 これ以外にも複数のbuild-in appenderが用意されていて、ファイル出力を行なったりslackに投げたりとかできます。

https://log4js-node.github.io/log4js-node/appenders.html

appenderを設定するには、log4js.configure()を使います。公式サイトにあるfile出力を追加する例だと次のような形になってます。

const log4js = require('log4js');
log4js.configure({
  appenders: {
    out: { type: 'stdout' },
    app: { type: 'file', filename: 'application.log' }
  },
  categories: {
    default: { appenders: [ 'out', 'app' ], level: 'debug' }
  }
});

デフォルト設定と同じものですが、outの方で説明します。

appendersの中に {out : {type: 'stdout'}}という項目がありますが、これでoutという名前のstdout appenderを定義しています。

さらに、categoriesの中にあるdefault: appenders: ['out']の部分でdefaultカテゴリのログをoutに渡すという指定をしています。

この例でもやっていますが、1つのカテゴリに対して複数のappenderを指定することができます。また、category毎に異なる設定を渡すこともできるので、一般的な出力を画面に出して、セキュリティログはファイルだけに出力というような設定もappendersの指定だけでできます。

さらに loglevel filterというappenderを使うと、info以上はコンソールに出して、debug以上をファイルに記録というような切り分けもできます。

https://log4js-node.github.io/log4js-node/logLevelFilter.html

layoutの設定

デフォルトのレイアウトは [時刻] [ログレベル] カテゴリ - ログ出力 というような整形を行ないます。

こちらもいくつかbuilt-in設定がありますが、appender毎にデフォルトのlayoutが指定されているので、built-in appenderを使うのであれば、pattern layout以外は使う機会は無いと思います。

https://log4js-node.github.io/log4js-node/layouts.html

一応layoutの指定方法を公式のドキュメントからもってくると次のような形でappenderの設定時に指定しています。

log4js.configure({
  appenders: { out: { type: 'stdout', layout: { type: 'basic' } } },
  categories: { default: { appenders: ['out'], level: 'info' } }
});

この例では、outという名前のstdout appenderに対してbasic layoutを使うことを指定しています。

要するに、コンソール出力をカラーから白黒に変えてるわけですね。

カスタムappender

さらに、独自のappenderを作成することもできます。

公式ドキュメントのappenderとlayoutの説明の一番下に一応説明があるので、まずはこれに目を通しましょう。

https://log4js-node.github.io/log4js-node/appenders.html https://log4js-node.github.io/log4js-node/layouts.html

ちょっと分かり難いですが、カスタムappenderに関するドキュメントは別にこちらに用意されてます。

https://log4js-node.github.io/log4js-node/writing-appenders.html

appenderの実体は、次のような引数を持つ関数です。

(config, layouts, findAppender, levels)=>{}

それぞれの引数の意味は次のとおり

  • config log4js.configure()で渡されてきた設定のうち、そのappenderに関するもの
  • layouts 有効なlayoutの一覧
  • findAppender 名前から他のappenderを取り出す関数(loglevelFilterのように最終的な出力を他のappenderに依存している時に使う)
  • levels 有効なレベル定義の一覧

また、この関数はloggingEventを第一引数とする関数を返す必要があります。

実際に自分でappenderを書く時には、こちらにあるbuilt-in appenderの実装を見ながら書きましょう。

log4js-node/lib/appenders at master · log4js-node/log4js-node · GitHub

通常のappenderを作るならstdout、別のappenderを呼び出すフィルタ的なものを作る時はloglevel filterあたりを参考にするのが読みやすいのでお勧めです。

実際にこのように定義したカスタムappenderを使うためには、公式ドキュメントにあるように次のような設定をします。

const myAppenderModule = {
  configure: (config, layouts, findAppender, levels) => { /* ...your appender config... */ }
};
log4js.configure({
  appenders: { custom: { type: myAppenderModule } },
  categories: { default: { appenders: ['custom'], level: 'debug' } }
});

ここでは、myAppenderModuleという名前でカスタムappenderを定義し、custom という名前でこのappenderを登録して、defaultカテゴリのdebug以上のレベルで使うように指定しています。

カスタムlayout

さらにさらに、独自のレイアウトも追加することができます。

こちらはAPIが用意されていて、log4js.addLayout()という関数で定義します。

addLayoutは第一引数にレイアウトの名前、第二引数にレイアウト関数を返す関数を指定します。レイアウト関数は第一引数にlogEventオブジェクトを受け取って、整形した文字列を返す関数です。こちらはappenderと違って公式ドキュメントの"Custom Layout Example"という項目にコードの例まで載っているので、こちらを見てください。

実際に使う時は、appenderと同様にlog4js.configureの中で指定する必要があります。

まとめ

log4jsはnodejs用の二大ロギングモジュールの片割れです*3

built-inのappenderとlayoutの使い方については解説記事も多いのですが、以前独自のappenderとlayoutを作った時に公式のドキュメントを探し出すのにも苦労したので、この機に解説してみました。

公式のslackで数ヶ月前に「ちょっとサポートする時間がとれんから誰か手伝ってー」みたいな話が作者さんから流れてたので、我こそはと思う方はちょっとお手伝いしてみてはいかがでしょうか?

*1:実際は、logger.log() という関数があって、その第一引数にレベルを指定するのですが、logger.{レベル名}という名前で、logger.log.bind(logger, レベル名) を呼び出せるようになっています。

*2:この説明かえって分かり難い気がしてきた・・・

*3:もう一方はdebug、異論は認める :p

pythonでテキスト処理

この記事は Calendar for UdonTech Advent Calendar 2021 | Advent Calendar 2021 - Qiita の7日目の記事です。

日付変わってますが、まだ寝てないから7日目ということで :p

この前はベルターンスガチャの話を書きましたが、今日のWAS-INDの試合中にベルターンス選手ポンポントラブルで退場してっちゃったのでしばらくガチャが回せなさそうです。

というわけで仕方がないので、仕事の話でもちょっと書きます。

やりたいこと

さて、数値計算屋だったりHPC屋の仕事では、結構な頻度で数値実験をします。 この時、「同じプログラムを、ちょっとづつパラメータを変えながら何ケースも測定して、結果の出力から一部を抜きだしてきてまとめる」という作業がたいてい発生します。*1

私の場合、測定する時に

  1. 設定したパラメータと実行した日時をディレクトリ名に入れる
  2. 入力データをsedで書き変える
  3. プログラムを順番に実行 or バッチサーバに投げる

という手順でやるので一通りの測定が終わると、「微妙に名前の違う大量のディレクトリに、同じプログラムの出力ファイルがずらーっと入っている」という状態になります。

今日の本題はこの後の処理です。

今までは、こういう実験結果ファイルから grep, sed, tr, awkあたりを駆使したシェルスクリプトcsvファイルを作成 -> エクセルでグラフ作成 -> パワポに貼り付け という流れでまとめていました。

ここでシェルスクリプトを使っているのは 「 1つづつコマンドを実行して結果を見ながらパイプで継ぎ足していく」 というインクリメンタル泥縄式開発を採用しているという点が大きいです。

時々、「シェルスクリプトを捨てよう○○言語スクリプトで全部やろう」みたいな論をみかけますが、 こういう開発手法だとどうしてもシェルスクリプトに軍配が上がります。

しかし、最近はその後の「エクセルでグラフ作成」の部分が、pandas+matplotlibで表の整理&グラフ作成にとって代わられてるのと、jupyter notebookのおかげで、pythonでも泥縄式開発が容易になってきました。

そうすると、今までシェルスクリプトでやっていた部分も、pythonで書いてjupyter notebookにまとめてしまえば 中間ファイルも作らなくても済む(かもしれん)し、なにより後から流用する時に便利そうです*2

実際のスクリプトを移植する前段階として、とりあえずgrep, sed(tr), awkで普段やっている処理をpure pythonで書くにはどうすれば良いかというのをちょっと見ていこうと思います。

grepの代替

grepといっても、たいていの場合は複数のディレクトリに散らばったファイル相手に実行するので grep elapse **/*.txtみたいな形で実行しています。

これをpythonで代替しようとすると次のような形になりそうです。

import glob
def multigrep(keyword, filePatter):
    for filename in glob.glob(filePattern):
        with open(filename) as f:
            for line in f:
                if line.find(keyword)>=0:
                    print(filename,":",line) 

手元の環境でざっくりと時間をはかってみたところ

となりました。

なお、測定環境は以下のとおりです。

  • M1 mac book air (メモリ16GB)
  • jupyter lab desktop 3.2.4-3
  • python 3.8.12 | packaged by conda-forge | (default, Oct 12 2021, 21:50:38)

実験したディレクトリ下には613のディレクトリ、30400のファイルが存在する状況で測定しています。 *3

grepの方はコマンドラインで実行して/usr/bin/timeで測定した時のreal time、python版の方はjupyterlabから %timeで測定した時のWall timeなので、そのまま比較できるような数字ではないですがこの程度の時間差なら私にとっては十分実用の範囲内といえそうです。*4

sed, tr

続いて、sedとtrです。 最終的にはawkを使って、前のgrepの出力に含まれるディレクトリ名からパラメータや、ファイルに書かれていた値を取り出すんですが、その前に名前を正規化したり、不要な文字列を削ってawkの処理を*5軽くするために使います。

pythonだと単純に文字列メソッドのreplaceを使えば良いのですが、用途によっては{r,l,}stripなんかも使えそうです。

状況によって変わるので具体的な例で考えてみましょう。

たとえば、intel CPUのマシンでMPI16プロセス、OpenMP4スレッドで測定した時の実行時間が"output.txt"というファイルの"elapsed time"という行に書かれているとすると、前段のgrepを実行した後の出力には次のような行が含まれているはずです。

yyyymmdd_MPI16_OMP4_intel/output.txt:   elapsed time = 1.04e-5

ここからMPI、OMPの後の数字とintel, 最後の1.04e-5を取り出すという処理を考えます。

この場合、まずsedかtrで"_"区切りの文字列に変換します。

sedの場合

echo yyyymmdd_MPI16_OMP4_intel/output.txt:   elapsed time = 1.04e-5 |sed -e 's!/!_!' -e 's/:/_/' -e 's/=/_/'

trの場合

echo yyyymmdd_MPI16_OMP4_intel/output.txt:   elapsed time = 1.04e-5 |tr '/' '_'|tr ':' '_'|tr '=' '_'

可能な場合はtrでやることが多いですが、諸事情により全ての文字を変換しちゃうとマズイとか、区切りに使う文字が本来区切りとして使わない場所にあったりするとsedで別の文字に変換して保護するような使い方が多いです。

pythonの場合は単純に文字列メソッドのreplaceをつなげていけば良いので

"yyyymmdd_MPI16_OMP4_intel/output.txt:   elapsed time = 1.04e-5".replace("/","_").replace(":","_").replace("=","_")

でできますね。

awk

こちらも具体例ということで、sed,trで処理した結果の文字列から、MPIプロセス数、OpenMPスレッド数、実行マシン、実行時間 を取り出す処理を考えます。

awkの場合だとこんな感じですね。

echo yyyymmdd_MPI16_OMP4_intel_output.txt_ elapsed time _ 1.04e-5 |awk -F'_' 'BEGIN{OFS=","}{print $2,$3,$4,$7}'

pythonだと、splitして必要な要素だけをjoinすれば良いので

",".join([ v for i,v in enumerate("yyyymmdd_MPI16_OMP4_intel_output.txt_ elapsed time _ 1.04e-5 ".split('_')) if i in [1,2,3,6]])

実際には、ここでカンマ区切りの文字列にする必要は無くて、リストのままpandasのdataFrameに突っ込んでさらに後続の処理へと続くことになると思います。

まとめ

今まで、シェルスクリプトでやってたテキスト処理をpure pythonにする方法についてちょっと検討してみました。

実行時間とかメモリ使用量なんかは、実際に使うデータによって異なるので、pythonで全部処理するのが必ずしも良いとは言いませんが、私の場合は全部pythonでやってもなんとかなりそうな感触です。*6

*1:チューニングやデバッグ中なら同じプログラムというのが、同じコードから生成されたものではなくて、微妙に違うバージョンのプログラムになったりします。あと測定環境を変えて実行して比較するようなケースもあるので、同じプログラムというのは厳密に同じ実行ファイルを指すとは限りません

*2:ちょくちょく、後処理のスクリプトはあるんだけど、csvファイルが残ってなくて、どういうデータに整形すれば使えるのか分からないという悲しい事件が起きるのです・・・orz

*3:なお、この実行時間はマシンの状態やファイルのサイズによって大幅に異なると思われるので必要であればご自身が使う予定の環境で測定してください

*4:そもそも、今回はコンソールに出力してるけど、実際はこの出力内容をパイプで渡すなり、文字列として保持して次の処理へ渡すので、ここで測った時間よりはさらに早くなることが期待できます

*5:正確に言うとawkの処理を書く手間を

*6:ま、だめならsubprocessで・・・

basketball-reference.comの歩き方

この記事は Calendar for UdonTech Advent Calendar 2021 | Advent Calendar 2021 - Qiita の4日目の記事です。

私の担当日じゃないんですが、空いてるようなんで勝手に埋めときます :)

突然ですがみなさんガチャ回してますかー?

英霊だったり馬だったり刀剣だったり、世の中にはさまざまなガチャが溢れていますが、 我々ワシントンウィザーズファンは試合の度にベルターンスガチャを回しています!!

ワシントンウィザーズ(以下WASと表記)は、NBAバスケットボールチームのひとつです。 私は所詮、追いかけはじめて3年目のニワカなので、細かいことはwikipediaを見てください :)

ja.wikipedia.org

このチームには、ラトビアが誇る3ポイントシューター ダービス・ベルターンス(Dāvis Bertāns)という選手が居ます。

彼の特徴を簡単にまとめると

  • 顔がでかい
  • ベンチスタートで、ちょろっとでてきて3pをばしばし打つ
  • めっちゃクイックモーションで3P打てる
  • 体勢が崩れながらでも、普通に打てる
  • 去年から引き続きスランプ中
  • 時々神がかった確率で決める
  • 顔がでかい

という具合でエリートシューターなんですが、すごいムラのある選手です。今年は1回だけ大爆発した試合があってその時に某NBA youtuberさんに「ベルターンスガチャの引きが良かった」と評されてました。*1

さて、ガチャだということは排出率を表示する必要があります*2

ここからは、basketball-reference.comのデータを使って、つよつよベルターンスの排出率を求めてみましょう。

---- ここまで前フリ ----

データの入手

NBAの各プレイヤー、チームの統計情報は、公式が供給してくれてます。

stats.nba.com

が、ちょっとこのデータを取得するには、地道にコピペするしかないので、ここでは使いません。*3

代わりにbasketball-reference.comからデータをダウンロードしてきましょう。

www.basketball-reference.com

こちらのページに、ベルターンス個人のスタッツがまとめられています。

www.basketball-reference.com

このページの上の方にある "Dāvis Bertāns Overview" と書かれた行の"GameLogs"の部分にポインタを乗せると次のように各年の試合毎のスタッツ一覧のページへのリンクが表示されます。

f:id:n_so5:20211203233728p:plain

今年のページへ移動してちょっと下へスクロールすると、"Regular Season"と書かれた横に "Share & Export"という表示が見えます。

f:id:n_so5:20211203234109p:plain

またもや、マウスオーバーで下にリンクの一覧が表示されるので"Get as Excel Workbook"を選択するとxls形式でダウンロードできます。すぐ下にあるget table as CSVを選びたくなりますが、ここはグっと我慢してください。

f:id:n_so5:20211203234627p:plain

これを1年毎に繰り返していけば、NBAでの全試合のスタッツがダウンロードできます。が、とりあえずWASに移籍してきた2019-2020シーズン以降の3ファイルだけ取ってきます。*4 ファイル名は全部固定で、"sportsref_download.xls"となっているので適当にリネームしておいた方が安心できます。

データの読み込み

excelファイルなんだら、pandas.read_excelで良かろうと思いきや、xlrdで読むとUnsupported format, or corrupt file: Expected BOF record;と怒られます。

BOFでも先頭に付ければ解決するのかと思いつつ、一応エラーメッセージをぐぐって見たらpandas.read_htmlでhtmlとして読めとのこと

stackoverflow.com

うちの環境では、別途lxmlパッケージをインストールする必要がありましたが、この方法で回避できました。

ただし、read_html(ファイル名)とすると、0番目の要素にDataFrameが入ったリストが返ってくるので、全部取り出して連結する必要があります。

とまぁ日本語で書くと面倒なんですが、こんな感じのコードでカレントディレクトリにあるbasketlball-reference.comからダウンロードした全てのxlsファイルをまとめて1つのDataFrameにできます。

import pandas as pd
import glob
df=pd.concat([pd.read_html(f)[0] for f in glob.glob("*.xls")],ignore_index=True))

データ分析

さて、読み込んだDataFrameから色々と見てみましょう。

ベルターンスの場合、着目する情報は 3Pの確率("3P%"の列)、3Pの成功数("3P"の列)ぐらいのものです。3Pを高確率で決めてくれて、1試合あたりの本数も多ければ多いほど良いと考えます。*5

方針が決まったら、さくっとseabornでグラフを書いてみましょう。このコードは前のコードに続けて実行してください。( jupyter notebookでそれぞれのコードブロックをセルに書いて上から順に実行していく前提で書いてます)

import matplotlib.pyplot as plt
import seaborn as sns
sns.set(context='talk', style="ticks")
df["3P"]=pd.to_numeric(df["3P"],errors='coerce')
df["3P%"]=pd.to_numeric(df["3P%"],errors='coerce')*100
df=df[["3P","3P%"]].dropna()
sns.jointplot(x="3P", y="3P%", data=df)

f:id:n_so5:20211204010634p:plain

ふむ。

これを見ると、ベルターンスの成績は次のように分類できそうです。*6

  • SSRベルターンス: 3P% >80 && 3P > 5
  • SRベルターンス: 3P% >60 && 3P > 3
  • Rベルターンス: 3P% >40 && 3P > 3
  • Nベルターンス: その他

それでは、排出率を求めましょう。この期間中に彼が出場した試合数と、各ベルターンスの出現回数を数えます。 ちょっと直感的では無いコードですが、こんな感じで数えられます。df.count()すると各列の行数を数えてくれるので、totalと矛盾が無いか確認しときましょう。

SSR=((df['3P%'] > 80) & (df['3P'] > 5))
SR=((df['3P%'] > 60) & (df['3P'] > 3))
R=((df['3P%'] > 40) & (df['3P'] > 3))
N=((df['3P%'] <= 40) | (df['3P'] <= 3))
print("SSR:",SSR.sum())
print("SR:",SR.sum()-SSR.sum())
print("R:",R.sum()-SR.sum())
print("N:",N.sum())
print("total:", R.sum()+N.sum())

結果はっぴょー

SSR: 1
SR: 11
R: 23
N: 86
total: 121

ここ3シーズンで、121回ガチャをまわして、SSRベルターンスは1回引けました。といわけでSSRの排出確率は1/121・・・ではありませんね。

SSRの排出率をpと置くと、121回試行して1回だけあたっているので


 {} _ {121} C _ {1} p(1-p)^{120} = \frac{1}{121}

OK, Wolfram Alpha

f:id:n_so5:20211204222545p:plain

これ他にも p<1を満たす解がある可能性はあると思うんですが、p<1を指定すると標準の計算時間を超えちゃうので、SSRベルターンスの排出率は5%ということにしましょう。

え?0.007%の方はどうするのかって?

人間は見たい数字しか見ないのです(キリ

まとめ

ベルターンスがんばれー :)

*1:この記事は12/2のWSH-MINを観ながら書いてたんですが、この試合でも活躍してたので、今年は既に2回ほどつよつよベルターンスを引けてます :)

*2:いやない

*3:非公式なAPIは存在するようですが

*4:ここはレギュラーシーズンの結果しか無いので、プレイオフの結果も含めたいならさらに下の方にある"Playoffs"のとこからもダウンロードが必要です。でもここ3シーズンだと去年の1回戦敗退の結果しか無いので、無視します :)

*5:この基準で行くと、ビシっと活躍してたのに、早々にケガで退場しちゃったとか、クォーター毎に無理目のブザービーターをトライして全部外したとかでも評価が下がっちゃいますが、まー彼の給料の査定をしてるわけでも無いので目をつむってもらいましょうw

*6:URベルターンスは入ってません :p