2022年の振り返り

2022年の振り返り

実家にパソコンを持って帰ってきたけど、考えてみるとここにはパソコン作業をするための机がない。引っ越すときに机を持ち出したからだ。変な姿勢で書いている。

※ 今年・去年・来年がややこしくなりそうなので、2022年・2023年で表記を統一します。

一人暮らし

2022年の個人的に最大の変化。6月から一人暮らしを始めた。
面倒くさがりな性格のせいで、「一応生きていくことはできるが、使いにくくて不便」という状況になっているので、どうやって住みやすい暮らしにすれば良いかな……

仕事

主なプロジェクトは3つ。
第1のプロジェクトは4月まで。あんま覚えてない……仕事のコミュニケーションって大変ですねというに留めておくか。
第2のプロジェクトは9月途中まで。ここは大変だった。火消しに追われて、現職への入社以来初めて残業時間が要注意ラインを超えた。けど、振り返ってみると期待以上の働きができたと思う。チームリーダーとしての面目躍如である。データ分析系なら少人数チームの進捗管理して成果上げることできるんだな。
第3のプロジェクトは継続中。これまでは小規模データを手元(ローカルマシン上)で解析することが多かったが、AWS系のシステム構築だったりで初見要素が多い。まぁ慣れないなという感想に尽きる。
業務中は基本的にあんまり細かい反省はしないことが多い。「今の自分にはXXXが足りていない。その対処としてYYYを実行した。その結果としてZZZという成果を上げた」みたいな細かい分析はせずに「今の自分にできることをできる範囲で頑張るわ」という感じで生きているので、構造化面接とかで掘り下げられると困ってしまう。みんなそんな緻密に考えて仕事してるの?
職位も1個上がったけど、このままやっていくか考え中なので、データサイエンティストを探している会社がいましたら連絡ください。話しましょう。あ、エージェントは間に合ってるので不要です。

有給休暇も年5日(法律的に取得が義務付けられている最低ライン)だった。わざわざ有給休暇を取ってやることが思いつかないという理由もあり、取ったら業務の進捗に遅れをきたすかなと言う心配もあり。

自己学習

なんか2022年は全然やらなかった。
この技術ブログも復活させようと何度か月次目標に書いてみたりもしたのだが、実際の作業が伴わず記事を書き上げられなかった。
競技プログラミングAtCoder2022年に出たのは9月の1回だけGCJは辛うじて毎年恒例で参加したけど。
社内で週1で開催されている勉強会も、業務の打ち合わせとかぶるので参加できなくなって、そのまま不参加がデフォルトになってしまった。うーーん。
原因はよく分からない。コロナ禍のせい(リアルの勉強会が無くなった)か、一人暮らししたせい(暇があったらゲームするかゲーセンに行くかするようになった)か、転職したせい(業務している時間が長くなった)のどれかだと思うけど。
ともあれ、原因を分析するよりも事態を改善するのが先だと思うので、来年は技術ブログ復活の年にしたい……。
活発に技術の勉強をしているコミュニティを見つけて参加するのが良いのかなとボンヤリ思っている。
「技術の自己学習をしなければいけないと思っているようではダメ。真のエンジニアは勉強したくて仕方がないから暇さえあれば勉強してる」みたいな言説もあるけど、黙殺させてもらうことにする。

ついでにいうと読書も全然しなくなったんだよな。ここ1年で朝井リョウの「何者」を読んだくらいじゃないか?

ゲーム

音ゲー

数年に1回ある「メインの音ゲーが移り変わる」が発生した。 2022年の途中まではドラマニがメインだった。HIGH-VOLTAGEが稼働したのが2021年4月だ。そのちょっと前(NEX+AGE後半。2020年秋頃?)からドラマニメインだった。
けど、気づいたらDDRに移行していた。9〜10月ごろか?
HIGH-VOLTAGE gsv記録
5500目標だったけど達成しないうちに新作にFUZZ-UPに。こうなるとスキルが0にリセットされるのでモチベがダルいね。

DDRはskill attackを詰めてみたけど、2015年当時→2022年末で、14〜15のスコア能力は多少伸びたらしいので嬉しい。ただ17〜18のクリア能力は多分当時ほど戻っていない。今は18弱がクリアできるかできないかの実力だ。

skill attack 2015当時
skill attack 2022末

音ゲー以外

  • 原神:冒険者ランク59まで行ったのに螺旋最終12章を倒せないのって俺くらいじゃないですかね……まぁイベントが次々配信されているので惰性で続けている感覚。やめようかなとたまに思いつつも続けている。
  • オクトパストラベラー:2022年5月に買って、70時間ほど。シナリオは終盤に来たけどサブシナリオは全然取れていない。続編の2が2023年2月24日発売なのでその前にクリアしたいが……
  • 星のカービィ ディスカバリー:2022年5月に買って、最初の方で止めている。

2022年はなぜか歯の不調に悩まされた。

2022年の初めのほう、夕方になったら右下の歯がすごい痛むのに歯医者行っても異常なしと言われる(おそらく非歯原性歯痛)
→対処が分からなくて調べる
→何か調べたら筋膜を押すと良いらしい
→奥歯を外から押し込みすぎて歯並びが変わってしまったっぽい
→右上の歯に力が加わってフィステルになった(5月)

その後も色々とあり、 現状、口の中からやたらとパキパキという音がなるようになってしまった。 噛み合わせも安定しないし、舌が歯に変に当たって舌炎になることも多いし。歯医者は現状で行ってるから歯科矯正するか。残りの人生をこのめっちゃ不便な状態で過ごすことを考えたら必要な処置だとは思う。けど費用がかなり高い…… タイムマシンがあったら「奥歯を押し込んで歯並びを変えるのをやめろ」って過去の俺に言いたい。

総評

一人暮らしして変わるかなと思ったけどかなり順当な変化だった。一人暮らしして生きるためのこと(炊事・洗濯・掃除)をやり、気兼ねなく外出できるようになったので日本酒と音ゲー要素が増えた。

新規追加されたのはスポーツジム・筋トレ・プロテインの要素くらいか。 ピカチュウライチュウになったみたいな感じだよなって年末から思っている。(順当な・予想できる進化だよなー、ということ)もう少し意外性のある進化をしても良い気がする。
せっかく自分だけで完全に自由に使える時間と空間があるわけで。その自由が続く時間は(願わくは)そう長くはないので、使い方を考えていきたいね。

ひとまず以上です。

seabornのswarmplotで点の色を直接指定する

【注意】この記事は完成度70%くらいです。一部の図が張れていないので正しく表示されません。気が向いたら読める形にします。

seabornが好きだ。
特に指定しなくても、大抵の場合はきれいな色で美しいグラフを描画してくれるし、 matplotlibでは簡単に描けないような複雑なグラフも一発で作れる。

seabornを使って多数のグラフを別々に作った場合に、色を合わせたい場合がある。
例えば「1月のデータは赤、2月は青」という決まりでグラフを書きたい場合である。 一部のグラフでその決まりが崩れていると、読んで理解するのに時間がかかってしまうだろう。 そこで今回は、swarmplotで色を直接指定する方法について調べた。
seaborn公式ドキュメントのswamplot関数の説明はこちら

準備

import seaborn as sns
import matplotlib

# --------------------

Duplicate key in file PosixPath('/usr/local/lib/python3.8/site-packages/matplotlib/mpl-data/matplotlibrc'), line 258 ('font.family : Hiragino sans')
# 動作環境の確認
# print(pd.__version__)
# print(np.__version__)
print(sns.__version__)
print(matplotlib.__version__)
!python3 --version

# --------------------

0.11.0
3.3.1
Python 3.8.5
# https://seaborn.pydata.org/generated/seaborn.swarmplot.html
# ★styleの設定どうする?
tips = sns.load_dataset("tips")
tips.dtypes

# --------------------

total_bill     float64
tip            float64
sex           category
smoker        category
day           category
time          category
size             int64
dtype: object

※上記のdtypesで、dayなどのカラムが文字列型ではなくカテゴリカル型であることに注意、
(文字列型だと異なる挙動をする可能性もある。今回は検証していない。)

seabornのswarmplotで、色を指定する方法は2つある。
1つが、xとyのうち片方に指定したカテゴリに合わせて色を指定する方法である。
もう1つが、軸に指定したものとは別のカテゴリに合わせて色を指定する方法である。
この順に紹介する。

1つ目:軸に指定したカテゴリに合わせて色を指定したい

ax = sns.swarmplot(x="day", y="total_bill", data=tips)

xにdayを指定すると、xに応じて点に色が付く。
この色を直接指定したい。
例えば、Thurを赤で、Friを黒で、……のように具体的な色の希望があった場合、どうすればよいのか?
色を単純に入れ替えたいだけならば、hue_orderで順序を指定すれば良い……かと思ったが、hue_orderを用いてもx軸上の並びは変わらなかった。
(たぶん元データの中でdayのデータ型がcategoryなので、hue_orderを指定しても無視されて、categoryが優先される?)

ax = sns.swarmplot(x="day", y="total_bill", hue_order=["Fri", "Sun", "Sat", "Thur"], data=tips)

正解は 引数のうち、paletteを指定すればよい。

palette palette name, list, or dict
Colors to use for the different levels of the hue variable. Should be something that can be interpreted by color_palette(), or a dictionary mapping hue levels to matplotlib colors.
https://seaborn.pydata.org/generated/seaborn.swarmplot.html より

palette引数に指定できるのは、paletteの名前かlistかdictである。
「paletteの名前」はpastelやbrightなどである。カラーパレットに関する公式ドキュメントの説明を参照。今回は具体的な色を指定したいので「paletteの名前」は使えない。 listを使って指定してみよう。以下のようになる。 listの中身である「具体的なそれぞれの色」の指定方法はいくつかあるが、今回は色の名前(文字列)を使う。

ax = sns.swarmplot(x="day", y="total_bill", palette=["purple", "green", "orange", "skyblue"], data=tips)

リストだと、どの値がどの色になるのか分かりづらい。dictも指定できて、この場合は対応関係が明確になる。

ax = sns.swarmplot(x="day", y="total_bill", palette={"Thur": "purple", "Fri": "green", "Sat": "orange", "Sun": "skyblue"}, data=tips)

ちなみにpaletteをリストで指定し、長さが足りない場合、繰り返しになる

ax = sns.swarmplot(x="day", y="total_bill", palette=["purple", "green"], data=tips)

辞書でkeyがない場合は、エラーになる。そのdayを何色で塗ればいいか分からないからね。

# 辞書のkeyにFriがないのでエラー 
ax = sns.swarmplot(x="day", y="total_bill", palette={"Thur": "purple", "Sat": "orange", "Sun": "skyblue"}, data=tips)

# --------------------

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-9-c9f97ba5b082> in <module>
      1 # 辞書のkeyにFriがないのでエラー
----> 2 ax = sns.swarmplot(x="day", y="total_bill", palette={"Thur": "purple", "Sat": "orange", "Sun": "skyblue"}, data=tips)

/usr/local/lib/python3.8/site-packages/seaborn/_decorators.py in inner_f(*args, **kwargs)
     44             )
     45         kwargs.update({k: arg for k, arg in zip(sig.parameters, args)})
---> 46         return f(**kwargs)
     47     return inner_f
     48 
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in swarmplot(x, y, hue, data, order, hue_order, dodge, orient, color, palette, size, edgecolor, linewidth, ax, **kwargs)
   2989         warnings.warn(msg, UserWarning)
   2990 
-> 2991     plotter = _SwarmPlotter(x, y, hue, data, order, hue_order,
   2992                             dodge, orient, color, palette)
   2993     if ax is None:
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in __init__(self, x, y, hue, data, order, hue_order, dodge, orient, color, palette)
   1171         """Initialize the plotter."""
   1172         self.establish_variables(x, y, hue, data, orient, order, hue_order)
-> 1173         self.establish_colors(color, palette, 1)
   1174 
   1175         # Set object attributes
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in establish_colors(self, color, palette, saturation)
    304                 else:
    305                     levels = self.hue_names
--> 306                 palette = [palette[l] for l in levels]
    307 
    308             colors = color_palette(palette, n_colors)
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in <listcomp>(.0)
    304                 else:
    305                     levels = self.hue_names
--> 306                 palette = [palette[l] for l in levels]
    307 
    308             colors = color_palette(palette, n_colors)
KeyError: 'Fri'

2つ目:軸に指定したものとは別のカテゴリに合わせて色を指定したい

# Color the points using a second categorical variable:
# 2つ目のカテゴリカル変数を用いて、点の色を指定する
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", data=tips)

xにdayというカテゴリカル変数を指定して、色のパラメータhueにはsexという別のカテゴリカル変数を指定するパターン。 色を単純に入れ替えたいだけならば、hue_orderで順序を指定すれば良い。以下のようになる。

ax = sns.swarmplot(x="day", y="total_bill", hue="sex", data=tips, hue_order=["Female", "Male"])

次に、「男性が緑、女性が紫」のように、直接色を指定したい場合はどうすればよいだろうか? palette変数だよなー多分。
palette palette name, list, or dict
Colors to use for the different levels of the hue variable. Should be something that can be interpreted by color_palette(), or a dictionary mapping hue levels to matplotlib colors.

ax = sns.swarmplot(x="day", y="total_bill", hue="sex", palette=["green", "purple"], data=tips)

リストだと、どの値がどの色になるのか分かりづらい。dictも指定できて、この場合は対応関係が明確になる。

ax = sns.swarmplot(x="day", y="total_bill", hue="sex", palette={"Male": "green", "Female": "purple"}, data=tips)

# https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.to_hex.html
    
print(matplotlib.colors.to_hex("green"))
print(matplotlib.colors.to_hex("purple"))

# --------------------

#008000
#800080
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", palette={"Male": "#008000", "Female": "#800080"}, data=tips)




余談:color変数の挙動

color matplotlib color, optional
Color for all of the elements, or seed for a gradient palette.

ax = sns.swarmplot(x="day", y="total_bill", color="red",  data=tips)

png

なぜかFemaleの色だけを赤に指定するっぽい?

# Color the points using a second categorical variable:
# 2つ目のカテゴリカル変数を用いて、点の色を指定する
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", color="red", data=tips)

png

ではcolorとして色の配列を渡せば良さそうに見えるが、それではエラーになる。

# Color the points using a second categorical variable:
# 2つ目のカテゴリカル変数を用いて、点の色を指定する
ax = sns.swarmplot(x="day", y="total_bill", hue="sex", color=["red", "gray"],  data=tips)

# --------------------

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-9e9e98d7907f> in <module>
      2 # 2つ目のカテゴリカル変数を用いて、点の色を指定する
      3 
----> 4 ax = sns.swarmplot(x="day", y="total_bill", hue="sex", color=["red", "gray"],  data=tips)

/usr/local/lib/python3.8/site-packages/seaborn/_decorators.py in inner_f(*args, **kwargs)
     44             )
     45         kwargs.update({k: arg for k, arg in zip(sig.parameters, args)})
---> 46         return f(**kwargs)
     47     return inner_f
     48 
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in swarmplot(x, y, hue, data, order, hue_order, dodge, orient, color, palette, size, edgecolor, linewidth, ax, **kwargs)
   2989         warnings.warn(msg, UserWarning)
   2990 
-> 2991     plotter = _SwarmPlotter(x, y, hue, data, order, hue_order,
   2992                             dodge, orient, color, palette)
   2993     if ax is None:
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in __init__(self, x, y, hue, data, order, hue_order, dodge, orient, color, palette)
   1171         """Initialize the plotter."""
   1172         self.establish_variables(x, y, hue, data, orient, order, hue_order)
-> 1173         self.establish_colors(color, palette, 1)
   1174 
   1175         # Set object attributes
/usr/local/lib/python3.8/site-packages/seaborn/categorical.py in establish_colors(self, color, palette, saturation)
    293                     colors = light_palette(color, n_colors)
    294                 elif self.default_palette == "dark":
--> 295                     colors = dark_palette(color, n_colors)
    296                 else:
    297                     raise RuntimeError("No default palette specified")
/usr/local/lib/python3.8/site-packages/seaborn/palettes.py in dark_palette(color, n_colors, reverse, as_cmap, input)
    541 
    542     """
--> 543     rgb = _color_to_rgb(color, input)
    544     h, s, l = husl.rgb_to_husl(*rgb)
    545     gray_s, gray_l = .15 * s, 15
/usr/local/lib/python3.8/site-packages/seaborn/palettes.py in _color_to_rgb(color, input)
    465         color = xkcd_rgb[color]
    466 
--> 467     return mpl.colors.to_rgb(color)
    468 
    469 
/usr/local/lib/python3.8/site-packages/matplotlib/colors.py in to_rgb(c)
    344 def to_rgb(c):
    345     """Convert *c* to an RGB color, silently dropping the alpha channel."""
--> 346     return to_rgba(c)[:3]
    347 
    348 
/usr/local/lib/python3.8/site-packages/matplotlib/colors.py in to_rgba(c, alpha)
    187         rgba = None
    188     if rgba is None:  # Suppress exception chaining of cache lookup failure.
--> 189         rgba = _to_rgba_no_colorcycle(c, alpha)
    190         try:
    191             _colors_full_map.cache[c, alpha] = rgba
/usr/local/lib/python3.8/site-packages/matplotlib/colors.py in _to_rgba_no_colorcycle(c, alpha)
    263         raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
    264     if len(c) not in [3, 4]:
--> 265         raise ValueError("RGBA sequence should have length 3 or 4")
    266     if not all(isinstance(x, Number) for x in c):
    267         # Checks that don't work: `map(float, ...)`, `np.array(..., float)` and
ValueError: RGBA sequence should have length 3 or 4



    
    
  

pandasの時系列カラムの時刻を特定書式の文字列に変換する方法

pandasの時系列カラムの時刻を特定書式の文字列に変換する方法

最近、このような状況が発生した。

  • データ分析用にダミーの簡単なデータを作る必要がある
  • そのデータは時刻カラムを含む
  • 時刻カラムは、タイムゾーンが設定されていて、UTCである
  • 実際のデータの表示書式はYYYY-MM-DDThh:mm:ssZ の形式 (例 2020-07-27T02:12:40Z)であるため、ダミーデータについても同じ書式で作成したい
  • どうすれば実現できるか?

注意:以下の説明で、「time zone naive = タイムゾーンが設定されていない」「time zone aware = タイムゾーンが設定されている」という意味である。


時刻表現 TやZの意味 | No pain,No gain.
で書いてあるように、2020-07-27T02:12:40Z という時刻の形式がある。 時刻を表現するときの国際的な規格として定められている、ISOもしくはRFCに従った形式である。
ISO規格だと ISO 8601
RFC規格だとRFC 3339
らしい。(ほぼ同じと見ていいらしい。Wikipediaの情報だけど)
pandasの時刻データをこの形で作る方法を調べた。

準備

import pandas as pd
import datetime
pd.options.display.notebook_repr_html = False  # jupyter notebook上での出力形式を制御するために書いています。無くても動きます。
# 動作環境の確認
print(pd.__version__)

# --------------------

1.1.2

時刻の列として、適当な時刻を3つほど作成する。datetime オブエジェクトを作り、日付と時までを適当に埋めよう。

datetime_list = [
    datetime.datetime(2022, 1, 2, 3, 0),
    datetime.datetime(2022, 2, 3, 4, 0),
    datetime.datetime(2022, 3, 4, 5, 0),
]
val_list = [10, 30, 20]
df_datetime = pd.DataFrame({
    'datetime'    : datetime_list,
    'val' : val_list
})
df_datetime

# --------------------

             datetime  val
0 2022-01-02 03:00:00   10
1 2022-02-03 04:00:00   30
2 2022-03-04 05:00:00   20

pandasの時刻カラムのタイムゾーン有無を調べる

自分の理解を整理するために、Q&Aの形式で書いていく。

Q1. このDataFrameのdatetimeカラムは、タイムゾーンがある時刻か、ない時刻か?
A1. タイムゾーンがない時刻である。

Q2. タイムゾーンがないということはどうして分かるのか?
A2. 以下2つの方法がある。
1つ目の方法は、カラムを調べることである。Series(カラム)にタイムゾーンがないことは、Seriesのdtypeを見れば分かる。

https://pandas.pydata.org/docs/user_guide/timeseries.html#time-zone-series-operations
A Series with time zone naive values is represented with a dtype of datetime64[ns].
A Series with a time zone aware values is represented with a dtype of datetime64[ns, tz] where tz is the time zone.

拙訳:タイムゾーンが設定されていない値を持つSeriesは、datetime64[ns]というdtypeで表される。

タイムゾーンが設定されている値を持つSeriesは、datetime64[ns, tz]というdtypeで表される。ここで、tzはタイムゾーンである。

df_datetime.dtypes

# --------------------

datetime    datetime64[ns]
val                  int64
dtype: object

2つ目の方法は、入っている時刻データを調べることである。

https://pandas.pydata.org/docs/user_guide/timeseries.html#time-zone-handling
By default, pandas objects are time zone unaware:

拙訳:デフォルトでは、pandasのオブジェクトにはタイムゾーンが設定されていない。

この公式ドキュメントによれば、tzという属性がNoneならタイムゾーンが設定されてないようだ。見てみよう。

datetime1 = df_datetime.loc[0, 'datetime']
datetime1

# --------------------

Timestamp('2022-01-02 03:00:00')
# 注:datetime1.tzと単に書くと、jupyter notebook上で結果のNoneが表示されないので、明示的にprintをつけてNoneを表示させている。
print(datetime1.tz)

# --------------------

None

どちらの方法にせよ、datetimeカラムにはタイムゾーンが設定されていないことが分かった。 さて、欲しいデータはUTCなので、タイムゾーンを設定しよう。

pandasの時刻カラムにタイムゾーンを設定する

Q3. 下記のコードでは、タイムゾーンを設定しようとしてSeries.tz_localize()を使っている。なんでエラーになるの?
A3. Series.tz_localize()はindexの時刻をローカライズ処理するため。Seriesの値をローカライズするには、Series.dt.tz_localize()を使う。

https://pandas.pydata.org/docs/reference/api/pandas.Series.tz_localize.html Localize tz-naive index of a Series or DataFrame to target time zone.
This operation localizes the Index. To localize the values in a timezone-naive Series, use Series.dt.tz_localize().

拙訳:SeriesまたはDataFrameの、タイムゾーンの設定されていないindexを指定されたタイムゾーンローカライズする。
この操作はインデックスをローカライズする。タイムゾーンの設定されていないSeriesの値をローカライズするには、Series.dt.tz_localize()を使うこと。

……と公式ドキュメントに書いてあるとおりで、Seriesに対して直接tz_localizeを実行しようとindexの時刻を変更しようとする。
今回はindexが時刻ではなくて数値なので「(indexの時刻を変更しようとしたら)indexが時刻じゃないんだけど」とエラーが出ている。

df_datetime['datetime_utc'] = df_datetime['datetime'].tz_localize(tz='UTC')

# --------------------

---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-10-d343d953d675> in <module>
    ----> 1 df_datetime['datetime_utc'] = df_datetime['datetime'].tz_localize(tz='UTC')
    
    /usr/local/lib/python3.8/site-packages/pandas/core/generic.py in tz_localize(self, tz, axis, level, copy, ambiguous, nonexistent)
       9643             if level not in (None, 0, ax.name):
       9644                 raise ValueError(f"The level {level} is not valid")
    -> 9645             ax = _tz_localize(ax, tz, ambiguous, nonexistent)
       9646 
       9647         result = self.copy(deep=copy)
    /usr/local/lib/python3.8/site-packages/pandas/core/generic.py in _tz_localize(ax, tz, ambiguous, nonexistent)
       9625                 if len(ax) > 0:
       9626                     ax_name = self._get_axis_name(axis)
    -> 9627                     raise TypeError(
       9628                         f"{ax_name} is not a valid DatetimeIndex or PeriodIndex"
       9629                     )
    TypeError: index is not a valid DatetimeIndex or PeriodIndex
# https://pandas.pydata.org/docs/reference/api/pandas.Series.dt.tz_localize.html
df_datetime['datetime_utc'] = df_datetime['datetime'].dt.tz_localize(tz='UTC')
df_datetime

# --------------------

             datetime  val              datetime_utc
0 2022-01-02 03:00:00   10 2022-01-02 03:00:00+00:00
1 2022-02-03 04:00:00   30 2022-02-03 04:00:00+00:00
2 2022-03-04 05:00:00   20 2022-03-04 05:00:00+00:00

Q4. Series.dt.tz_localize()を使って時刻変換したけど、このdtって何?
A4. dtは時刻形式のSeriesに対するアクセサ(accessor)である。
Series.dt.xxx という形で、時刻情報の一部を抽出したり、今回のtz_localizeのように時刻関係のメソッドを使ったりできる。
Series.dt.xxx の一覧は https://pandas.pydata.org/docs/reference/series.html#datetimelike-properties
dtについては https://pandas.pydata.org/docs/user_guide/basics.html#dt-accessor を参照。

さて、UTCに設定したら希望の書式になってくれるかと思ったが、そうではなかった。 2022-01-02 03:00:00+00:00 という形式になってしまった。
2022-01-02T03:00:00Z という形式が欲しいんだけど。

Q5. 日付と時刻の間にあるTはどういう意味? 半角空白の場合と何が違うの?
A5. ISO 8601では日付と時刻の間にTという文字を書く必要がある。(半角空白にすることは認められていない)
Q6. 時刻の末尾にあるZはどういう意味? +00:00と何が違うの?
A6. Zと+00:00 はどちらもISO 8601で認められた表記法で、タイムゾーンUTCであることを示す。

df_datetime.dtypes

# --------------------

datetime             datetime64[ns]
val                           int64
datetime_utc    datetime64[ns, UTC]
dtype: object
# で、このままcsvに出力しても希望通りの形式にはならない。
df_datetime.to_csv("temp1.csv")
# csvの中身を表示する
! cat temp1.csv

# --------------------

,datetime,val,datetime_utc
0,2022-01-02 03:00:00,10,2022-01-02 03:00:00+00:00
1,2022-02-03 04:00:00,30,2022-02-03 04:00:00+00:00
2,2022-03-04 05:00:00,20,2022-03-04 05:00:00+00:00

時刻が特定の書式になっているCSVを作る2つの方法

3つの方法が考えられる。

  • 時刻のデータ(dtype datetime64[ns, UTC])のまま、表示方法を変更する。
  • 時刻のデータをcsvに保存する際に、時刻形式を変更する。
  • 時刻のデータから、希望する形式の文字列に変換する。

このうち2番目と3番目の方法は実現可能である。

Q7. 時刻のデータ(dtype datetime64[ns, UTC])のまま、表示方法を変更する方法はあるのか?
A7. 多分ないと思う。あったら教えて下さい。

時刻のデータをcsvに保存する際に、時刻形式を変更する方法

Q8. 時刻のデータをcsvに保存する際に、時刻形式を変更する方法はあるのか?
A8. ある。to_csvの引数にdate_formatを指定する。

# to_csvの引数にdate_formatを指定すると、csvに書き出すときに時刻を希望の書式にすることができる
df_datetime.to_csv("temp2.csv", date_format="%Y/%m/%dT%H:%M:%SZ")
# csvの中身を表示する
# date_format引数はdatetime,datetime_utc の両方の列に適用されている
! cat temp2.csv

# --------------------

,datetime,val,datetime_utc
0,2022/01/02T03:00:00Z,10,2022/01/02T03:00:00Z
1,2022/02/03T04:00:00Z,30,2022/02/03T04:00:00Z
2,2022/03/04T05:00:00Z,20,2022/03/04T05:00:00Z

時刻のデータから、希望する形式の文字列に変換する方法

Q9. 時刻のデータをcsvに保存する際に、時刻形式を変更する方法はあるのか?
A9. ある。Series.dt.strftime() を使ってフォーマットを指定する。

Q10. 出来上がったカラムのdtypeはどうなってるのか?
A10. 文字列、objectである。

df_datetime['datetime_utc'].dt.strftime("%Y/%m/%dT%H:%M:%SZ")

# --------------------

0    2022/01/02T03:00:00Z
1    2022/02/03T04:00:00Z
2    2022/03/04T05:00:00Z
Name: datetime_utc, dtype: object

最初は訳わからなくてこれで作ってた。

df_datetime['datetime_utc'].apply(lambda t: t.to_pydatetime().strftime("%Y/%m/%dT%H:%M:%SZ"))

# --------------------

0    2022/01/02T03:00:00Z
1    2022/02/03T04:00:00Z
2    2022/03/04T05:00:00Z
Name: datetime_utc, dtype: object

一応、上のやり方を解説しておこう。
Seriesに対してapplyを使うので、tに該当するのは、 datetime64[ns, UTC] 型の1つの時刻である。
to_pydatatime()はPandasのTimestampオブジェクトをPythonのdatetimeオブジェクトに変換するもの。
https://pandas.pydata.org/docs/reference/api/pandas.Timestamp.to_pydatetime.html
で、datetimeオブジェクトに対してstrftime()関数で文字列に変換している。


以上、2つの方法で、YYYY-MM-DDThh:mm:ssZ という形式で時刻を出力することができた。

……これ実は、手動で「Z」という文字を付け加えているから、「タイムゾーン情報」としてZという文字を付加しているわけではない。タイムゾーンを設定しなくても指定書式でcsvが作れるな。
まぁ、試行錯誤の結果ということで、このままにしておきます……。


しかし今回調べてみると、 Series.dt.xxx でできることが意外と多かった。strftimeを使うと文字列に変換もできるのか。

python標準のdatetimeと対応関係を見てみよう。 例えば、df_datetime['datetime_utc'].dt.hourの場合。

python_dt = datetime.datetime(2022, 1, 2, 3, 0)
python_dt.hour

# --------------------

3
df_datetime['datetime_utc'].dt.hour

# --------------------

0    3
1    4
2    5
Name: datetime_utc, dtype: int64

こう見ると、「python_dt」と「df_datetime['datetime_utc'].dt」が対応している。

次に、df_datetime['datetime_utc'].dt.strftime()の場合。

python_dt.strftime("%Y/%m/%dT%H:%M:%SZ")

# --------------------

'2022/01/02T03:00:00Z'
df_datetime['datetime_utc'].dt.strftime("%Y/%m/%dT%H:%M:%SZ")

# --------------------

0    2022/01/02T03:00:00Z
1    2022/02/03T04:00:00Z
2    2022/03/04T05:00:00Z
Name: datetime_utc, dtype: object

これも、「python_dt」と「df_datetime['datetime_utc'].dt」が対応している。
Series.dtは単なるアクセサであるが、 「Series.dt は、PythonのdatetimeオブジェクトからなるSeriesのようなもの」と考えておくと、対応が分かりやすいのかもしれない……?
それでは。

Kivyを触ってみた

触ってみたきっかけ

GitHubの言語Pythonのトレンド の中にあったのでちょっと触ってみようと思った。

Kivyって何

Pythonの公式ドキュメント内のFAQの中のグラフィックユーザインターフェース FAQには、 Tkinter、Qtなどと並んでKivyの名前がある。これらは、パソコン上でGUIを作るためのライブラリらしい。

公式ドキュメント

https://kivy.org/doc/stable/ :公式ドキュメント
https://pyky.github.io/kivy-doc-ja/ :公式ドキュメントを有志が日本語訳したもの (Kivyは2022年1月時点で2.0.0が最新である。日本語版は「2017年5月現在Kivy1.10に対応しております。」と書いてあり、情報が古いことには注意が必要。)

環境

MacBook Pro (16-inch, 2019)
MacOS Catalina (10.15.7)

Kivyのインストール (Getting Started » Installing Kivy)

公式ドキュメントの指示通りにインストールした。

% python3 -m venv kivy_venv
% source kivy_venv/bin/activate

(kivy_venv) % python --version 
Python 3.8.5
(kivy_venv) % python3 --version
Python 3.8.5
(kivy_venv) % python -m pip install kivy[base] kivy_examples 
zsh: no matches found: kivy[base]
(kivy_venv) % python -m pip install 'kivy[base]' kivy_examples
Collecting kivy[base]
……(以下略、インストール完了)

一点詰まったのは、公式ドキュメントどおりにpython -m pip install kivy[base] kivy_examplesと実行してもエラーになってしまったことである。
"zsh no matches found kivy base"で検索して以下を発見。

python - zsh: no matches found: requests[security] - Stack Overflow

zshでは[ ]の記号が特別な意味(正規表現と同様の使い方)を持つため、このままだと実行に失敗する。
特別扱いを避けて期待通りの動作にさせる方法はいくつかあるようだ。今回は、kivy[base]という文字列全体を引用符で囲むという方法を用いた。

インストール結果は以下のようになる。

(kivy_venv) % pip freeze
certifi==2021.10.8
charset-normalizer==2.0.9
docutils==0.18.1
idna==3.3
Kivy==2.0.0
Kivy-examples==2.0.0
Kivy-Garden==0.1.4
Pillow==9.0.0
Pygments==2.11.1
requests==2.26.0
urllib3==1.26.7

python kivy_venv/share/kivy-examples/demo/showcase/main.py
と打つと、デモ用のアプリが立ち上がる。画面を切り替えると、ボタンやチェックボックスプログレスバーなど、いろいろな機能のデモができる。

インストールの次に見るべきページは何か?

ここの動線がちょっと複雑。 公式ドキュメント上で「Installing Kivy」の次は以下のページである。 https://kivy.org/doc/stable/gettingstarted/first_app.html
でこのページにはだいたいこう書いてある:「Pong Game Tutorialがあるからそれをやってね。基本的なことは書いてあるから。チュートリアルに従うと、単純なアプリケーションを作るよ。Pong Game Tutorialはロードマップの中で一番重要な記事だよ」

「ロードマップ」という単語が突然登場したから「何かそういうまとめ画像(例えばこれ みたいなやつ) があるの?」と思った。別にそういう「ロードマップ」はないらしい。

というわけでPong Game Tutorialに飛ぶと、以下のように書いてある。

Pong Gameのチュートリアルを始める前に

  • Kivyがインストールされていることを確認してね
  • Kivyアプリケーションを実行する方法を確認してね。わからないなら、Create an application を見てね

というわけで次に見るべきはCreate an applicationのようだ。

Create an application

https://kivy.org/doc/stable/guide/basic.html#quickstart
の'Hello World'アプリケーションを書き写して実行した。

Pong Game Tutorial

Pong Game Tutorial

拡張子kvのコードにシンタックスハイライトが適用されなかったので、VSCodeの以下をインストール。
https://github.com/sspaink/kivy-vscode

最初はコロンを使わずに間違ってイコールで書いてしまい、以下のエラーが出てきた。

 ...
      15:    Label:
      16:        font_size: 70
 >>   17:        center_x = root.width * 3 / 4
      18:        top: root.top - 50
      19:        text: "0"
 ...
 Invalid property name

        center_x = root.width * 3 / 4

次。
ページにはこう書いてある。

Note
COMMON ERROR: The name of the kv file, e.g. pong.kv, must match the name of the app, e.g. PongApp (the part before the App ending).

kvファイルの名前を間違えると動かないよ、という話だ。
間違えるとどうなるんだ……? と思って、pong.kv を invalid_name.kv に名前変更して main.pyを実行した。 特にエラーは出なかった。
しかし、その代わりに真っ黒な画面が出現した。
……なるほど、ファイル名がpong.kvだから自動的に関連付けて読み込まれるけど、違う名前だと無関係なファイルだと思われて読み込まれないようだ。

ボールを追加(Add the Ball)

ボールの位置を指定しているのは<PingGame>内部のcenter: self.parent.centerのように見えたので、 pong.kvの7行目の pos: self.posの必要性が無いように感じられた。これをコメントアウトして実行してみた。
すると、ボールが画面の左下に表示された。……え、何でだろう。謎だわ。


Pong Game Tutorial
の指示通りにコードを書いてPong(卓球)ゲームが完成するところまでやった。
本当は各ファイルのそれぞれのコードがどう繋がって、実際のゲームができたのとかしっかり理解するのが良いんだろうけど、久々の技術ブログなのでこれくらいで切り上げておく。
それでは。

ダンジョンエンカウンターズ プレイのメモ

www.jp.square-enix.com

記述の基本方針

  • 2時間ごとに章を作る。
  • 特にネタバレ配慮はしていないので注意してください。(プレイしたときのログというかメモという感覚で書いている)

基本情報・前提条件?

  • Switch版を買いました
  • 一回「これは仮だから」といいつつ8時間プレイして、セーブデータを消して新しく1からやり直した

まぁ割と安全めに倒した動きをしていると思います。 * 全滅したら救出しなきゃいけない→全滅しても良いように、たくさんのキャラを育てておく

18〜20時間

えーー全滅しました。2回目。
33階。敵はトレントとヘルプランツで合計4体くらいだったと思う。 また魔法攻撃ばっかりされて死んだパターン。

手持ちのレベルを上げて救出しに行くかと思ったけど、このゲームはレベルを上げると直接強くなるというものではない。(HPが上がるけどその効果は微々たるものだ)。 強くなるためには、以下のどちらかをする必要がある。

  • レベルが上がる→装備ポイントが上がる→今までできなかった強い装備ができるようになる
  • 新しい敵を倒す→店に新しい武器防具が並ぶ→それを買う→ステータスが上がる

1点目について。装備ポイントはとっくに十分上がっていて、「今までできなかった装備」はほとんど無いと思っている。
2点目について。全滅したメンバーも全員、ブロンズアーマー(1950)を装備していた。店にはそれより上の防具がない。いや、羽付き帽子(2100)はあるけど……この魔法防御の150の違いは誤差だろう。もっと強い防具を買わせろ……。 というわけで、レベルを上げても強くなれそうにない。これでは確実に救出できる自信がない。どうしろというんだろう。 アンクを32階で取ってたのが幸いで、これでアンクをアカデミーにおいておけば、救出して即0階に戻れる。

20時間:忘れた

20〜22時間

全滅部隊の救出は完了。淡々とレベル上げ。

22時間:35階まで、完全踏破25階

22〜24時間

  • 36階で初めての石化。「石化中」から時間が経つと石化になると思ってたけど、敵によっては「石化中」を経由せずにいきなり石化になるパターンもあるっぽい。
  • 石化解除を踏んでおいたおかげで、直近の場所がわかってよかった。31階の右下にある。
  • 防具、ブロンズヘルムとラメラーアーマーが並び始めた
  • 37階で地図問題5の答え 似たような箇所がたくさんある引っかけ問題。
  • 35階で仮想エレベーター上がりを獲得。
  • 37階は武器防具の店だけあって回復系マスがない。新しいパターンだ……
  • 37階で即死攻撃を喰らってHP回復アビリティ回数が尽きたので、武器防具を買い込んでから33階まで戻る。
    • マリアル7、マランダオール4、ラメラーアーマー、アーバレスト
  • 次に進むときは39階から0階に戻りたいな(どうせワープポイントあるでしょ?)

24時間:38階まで、完全踏破25階、最高レベル45

24〜26時間

  • 39階まで行って帰還成功。
  • 40階チラ見。30階に初めて行ったときは結構キツそうな印象だったけど、それと違って行けそうだ感がある。
  • 39階にアクセサリー店も並んでいるので、今後必要になったら39階にワープしてここに行けば良し。
  • 39階のデッドゲートに1人飛ばされる。行方不明になるのね。
  • 上位8人のうち、1人は石化→回復させたけど回収せず放置、1人は飛ばされて行方不明。……少し危ないから回収するか。
  • 犬のフラウを27階に行って犬のフラウを回収。
  • 36階で石化してた1人を回収。
  • そのままうろついてたら、36階の右下部分に各種回復があるじゃん!遠かったから気づかなかった。まぁそれ狙いでわざと外れた位置に配置してるんだろうけど。
  • 武器はアーバレストとオリハルコンがメインになった。

26時間:40階まで、完全踏破27階、最高レベル48

26〜28時間

全滅しました。3回目。
41階。サムライとかいう敵がHP直接攻撃を繰り返してきた。
1人がマンイーターに食われて、「行けるかな」と思ってそのまま進んでたらあえなく全滅。行けるかな、じゃねぇんだよ。

  • もう1人、38階でデッドゲートに飛ばされる。
  • レベルの高い順にトップ9人のうち、3人が41階で戦闘不能。1人が40階で食べられ。2人が38階と39階で飛ばされて行方不明。無事なのが3人。
  • あれ、これかなりヤバくない?
  • 武器はケンタウロスの弓矢とオリハルコンがメインになった。

28時間:42階まで、完全踏破30階、最高レベル50

28〜30時間

  • 飛ばしと石化を無効化して39階へ。
  • 2人が38階と39階で飛ばされて行方不明だったのを回収した。(飛ばされたときと同じフロアのランダムなマスに行く仕様だと思う。)
  • 38、21、22階を踏破。アビリティのポイントが必要なので。
  • 17、16、15階を踏破。アビリティのポイントが必要なので。
  • ふと15階を見ると地図問題2の答えだったので、アイテムを取得。
  • 4人パーティーで41階は行けた。けど、3人で行くとそこまでに全滅しそうで心配。
  • そのまま降りていったら44階で戦闘不能回復・HP回復は見つけた。
  • レベルの高い順にトップ9人のうち、3人が41階にいる(戦闘不能からは回復)。1人が40階にいる(食べられからは回復)。動かせるのが5人。

30時間:44階まで、完全踏破37階、最高レベル52

30〜32時間

  • ロリカセグメンタタとかいう舌を噛みそうな名前の防具を買って、39階から再出発。
  • 42階でトレジャー6。パウダーブラスター。あんま使えなさそう……
  • 44階、一方通行の転移装置を始めて見た。92階に飛ばすやつ。なんか92階がひどいってTwitterでちょっと見たんだよな……これか……もちろん踏まない。
  • 46→47→48階、一方通行の階段。そのまま無理やり行ったら49階まで行けるんじゃね? と思ったが、安全第一で44階まで戻る。
  • でも44階にはアビリティ回数回復がない……
  • 新武器が来た。
    • ペルセウスの弓矢 ← インプ ← 71、73 ← 44〜46階
    • ローリングエン ← スケルトン ← 6F、71 ← 44〜45階
  • 44〜46階をウロウロ。
  • 46階で初:モルモット。
    • 説明には「攻撃が失敗しやすくなる状態」と書いてあって、「たまに失敗するのかな」と思ったら、基本的に失敗である。ひどい。
    • 試しに記録を取ってみたら、15回連続で失敗して16回目に攻撃成功した。おそらく攻撃成功率は5%〜10%だと思われる。
    • 解除方法は不明なので放置……
  • 防具、アーメットとバルビュータが同時に入荷されてた。防具が一気に2段階上がるのは初めてだな…… あとはシルバーアーマーも入荷されてた。
  • モルモットと化したナンガパルをアカデミーに預ける。あと40〜41階の4人を回収。
  • レベルの高い順にトップ9人のうち、1人モルモット。

32時間:48階まで、完全踏破39階、最高レベル57

32〜34時間

  • 余計な戦闘をせず、49階まで行って戻ってこよう。
  • 39階に飛んですぐに仮想エレベーターで降りる。46階から49階まで行って帰還成功。
    • この瞬間が一番ホッとするね。
  • 48階トレジャー7。ただの4800G。しょぼい。
  • 50階。噂に聞いた、金を取られる床か。きっと、1マス600Gか800Gくらいだろ? 試しに乗ってみるか。「11100G」え? は????
  • 39階で83のマスを倒す→新しい武器 デュランダル が店に並んでるじゃん→83くらいのマスは(連続じゃなくて)一発なら倒せる。ちょっと下に潜って倒して強い武器を手に入れよう。
  • 54階、トレジャー9。攻撃速度アップね。
  • 55階に降りて進みだしたらいきなり落とし穴に落ちた。68階に行った。慌ててアンクで戻った。
  • 47階で「落とし穴発見」を見つけた。
  • 39階、地図問題7の答え。
  • レベルの高い順にトップ9人のうち、1人モルモット。

34時間:54階まで、完全踏破39階、最高レベル59

34〜36時間

  • 52階のバトルで全滅しそうになってギリギリ勝つ。危ない。
  • あちこち行ってるので武器防具の個数が揃わない。まとめるか。
  • 武器
  • 防具
    • アーメット ← ウェアウルフ ← 6A、6C ← --階近辺
    • バルビュータ ← ブラックベア ← 76、78 ← 46〜48階
    • シルバーアーマー ← ベヒーモス ← 76、79 ← 46〜48階
  • アクセサリー
    • コンドライト ← ジェネライザー ← 82〜86 ← 52階近辺
  • 「不明者発見」の値を見ながら52階をウロウロしてたら不明者発見。52, 68, 39 ントレヤナ。
  • 53階、地図問題8の答え。……専用装備だな。

全滅しました。4回目。
56階。8Bだとおもう、ウィザードと死のカードとラバー。魔法攻撃ばっかりされて死んだパターン。
結構深く潜っちゃって、大丈夫か微妙なところで「有料床回避」が見えて、1回バトルに勝てば到達できるから、これだけ取って帰ろう……と思ったけど帰れなかった。

36時間:56階まで、完全踏破41階、最高レベル59

  • レベルの高い順にトップ9人のうち、4人戦闘不能(56階)、1人モルモット。

36〜38時間

  • 49階をウロウロしてたら不明者発見。謎の掃除ロボじゃん! 49, 34, 88 K2000。連れて帰る。
  • 52, 68, 39 ントレヤナを連れて帰る。
  • K2000に「グラフェン加工」を装備させるとものすごい耐久力になるな。武器も片方を素手にしたけど、ゾンビ系を一撃で殺せるので悪くない。

38時間:57階まで、完全踏破41階、最高レベル64

38〜40時間

  • よし、そろそろ56階の戦闘不能4人を連れて帰ろう。ついでに「有料床回避」も取ってこよう。
  • 仮想エレベーターでどこに移動するか調査。
    • 0E転送→49階→仮想エレベーター下り→50階→仮想エレベーター下り→54階
    • 0E転送→49階→03のマス→仮想エレベーター下り→66階
    • 0E転送→49階→03の上のマス→仮想エレベーター下り→59階
    • 0E転送→49階→01のマスから50階に移動→仮想エレベーター下り→59階
    • 0E転送→49階→01のマスから50階に移動→1マス上に移動→仮想エレベーター下り→99階(!?)
    • 0E転送→49階→01のマスから50階に移動→1マス下に移動→仮想エレベーター下り→66階
    • 0E転送→49階→一番上まで移動→仮想エレベーター下り→61階
  • 上の検証をしてて59階で「精神統一」が取れた。なんて都合の良いアビリティなんだ。ただしコストは非常に高い。35もするのかよ。
  • 「さぁ帰ろう」と思ったらアンクを実行してなかった。しかも59階の転送装置までは辿り着けそうにない。死ぬかと思った。なんとか49階まで上がって戻ってこられた。
  • 56階の「有料床回避」は、下側からは入れない。上から「ナイトムーブ」で飛んでくる必要がある。
  • 56階で敵6人と戦って死にそうになる。敵の数は1〜6からランダムだと思うけど、最大数の6体出てくると結構きついな。
  • 59階から帰還成功。
    • 冷静によく見たらムーブで店にジャンプできる距離だった。隠し床で敵と戦う必要はなかった。
  • 幻城とは何。何で俺は城と戦ってるの。
  • 60階以降……クソだるい……床が見えない……

40時間:60階まで、完全踏破41階、最高レベル65 * レベルの高い順にトップ11人(K2000とントレヤナのぶんだけ増えた)のうち、1人モルモット。

40〜42時間

  • 56・57・59・61階をチマチマやって完全踏破。

42時間:63階まで、完全踏破45階、最高レベル66

  • レベルの高い順にトップ11人のうち、1人モルモット。

42〜44時間

  • 63階、HP全回復・全を入手。
  • 数値問題1と4を解く。
  • ついでに35階を踏破。
  • 行方不明者が35±4, 42, 75 だなと分かったので仮想エレベーターで下に。39, 42, 75 でモデナリを回収(ホクガクじゃないのか……)。
  • 49階をウロウロしていたら、行方不明者が49±15, 35, 84 だなと分かったので仮想エレベーターで上に。34, 42, 75でホクガクを回収。
  • ……ところでこの2人は行方不明時のレベルとフロアが一緒なんですが、もしかしてそういう法則があったりするか? だとするとレベル60のネコは60階にいるのか?
  • 60±2, 41, 41 に1人、その近くにもう1人……なんじゃこりゃ。
  • 61, 45, 50がオウユー。
    • 61階でレベル61。
  • 58, 41, 41 ネコ。59階でうろついてから1つ上に上がると楽。
    • 60階じゃないじゃん。ネコは例外か?

というわけで一気に4人(モデナリ・ホクガク・オウユー・ネコ)回収完了。

44時間:65階まで、完全踏破46階、最高レベル67

44〜46時間

  • レベル43のジョラスはどうせ43階やろ。行こう。回収成功。
  • 回収する前の戦闘で、ホクガクがモルモットになりやがった……なんか強いらしいのに……悲しみ……
  • 43階踏破したけど、ここ専用装備や特殊装備を売るショップがあるのね。
  • 64階、トレジャー10、ただのドレッドノートやんけ、つまらん。
  • 65階で迷ったので、調べておいたこれで66階に行って探検。この辺の敵がギリギリ倒せるくらいだ。
    • 0E転送→49階→03のマス→仮想エレベーター下り→66階
  • さて59階から降りるとどこに行くかは調べておこう。
    • 0F転送→59階→仮想エレベーター下り→60階→仮想エレベーター下り→68階
    • 0F転送→59階→14のマス→仮想エレベーター下り→60階→仮想エレベーター下り→62階
    • 0F転送→59階→15のマス→仮想エレベーター下り→62階→仮想エレベーター下り→66階
    • 0F転送→59階→16のマス→仮想エレベーター下り→63階→仮想エレベーター下り→78階
    • 0F転送→59階→17のマス→仮想エレベーター下り→63階→仮想エレベーター下り→67階
  • ネコのナイフ&フォークが強いという話。

46時間:69階まで、完全踏破47階、最高レベル68

46〜48時間

  • 69階から戻ってくる。
  • 66階でネコがモルモットになったんだけどー。しかも同時にロボが食べられたんだけどー。
  • モルモットの解除方法、未だに不明。
    • 解除マスがどこかにあるということは分かっているけど。イベントブックに書いてあるから。でも見たことはないね。
  • 69階の普通には到達できないところに「透明床が見える」アビリティがあるね……どうやるんだ
    • ランダムジャンプを繰り返して、行けることを願う
    • 座標を覚えておいて、上か下から仮想エレベーターで移動する
    • 正攻法でどう行けば良いのかよく分からん。それを探して66階まで行ったらこのザマだよ。
  • トレジャーリセット(FC)を初めて倒した。
    • これくらいまでキャラが育ってれば、攻撃を受ける前に倒すこともできるな。
  • 「透明床発見」を取りに行こう。
    • 69階、南は50〜51、東は42〜45。特に51, 42が7Fマス、51、45が02マス。を調べる。
    • 59階へ。え、51, 42のマスがあるじゃん。
    • そこから仮想エレベーターで63階→69階に行って、「透明床発見」を入手。
  • 透明床発見を装備して、そのまま69、68、67階を完全踏破。いやこれ があると100倍楽だな。

48時間:70階まで、完全踏破51階、最高レベル69

  • 2人がダンジョン内、そして3人がモルモット。特にネコとホクガクは早くモルモットを治して使いたいんだが〜〜……

48〜50時間

  • 強めの編成にして階数を進めに行こう。
  • HP満タンで攻撃力倍を初めて着けたら、めちゃ強い。どんどん倒せる。
  • 72階の途中で落とし穴で進めず、面倒になったので、仮想エレベーター下りを実行。84階へ。
  • 倒せそうな気がしたので1回だけ戦闘してみる。やっぱり倒せた。
  • 適当に仮想エレベーター上りを実行したら、79階に着いて、そこから転送装置を見つけて帰ってきてしまった。マジかよ。
  • 81階をウロウロしてたらやたらとトレジャーリセットに出会った。……もしかして、トレジャーリセットってトレジャーがある階層のみに出現する法則とかあるのか?
  • 50, 51, 52階を踏破。(50〜59階は、落とし穴見える + 有料床回避 + 透明床見える を付ける必要があるので、なかなか厄介だね)
  • 52階に謎の液体を売る店があったのに初めて気づいた。
  • そのまま53, 54, 55階を踏破。
  • 仮想エレベーターで遊んでいたら4階から一気に91階に着いた。なにこれ怖い。
  • 91階にトレジャーがいくつかあり、拾ったらイベントブック上の位置が上書きされた。なにこれ怖い。

50時間:81階まで、完全踏破57階、最高レベル76

50〜52時間

  • 80, 81階を完全踏破。

全滅しました(行方不明)。5回目。
74階の完全踏破を狙ってたけど、最後1マスが落とし穴だったんだよね。まぁ大丈夫でしょって言って落ちてみたらこのザマである(永遠に落ち続けて行方不明になった)。そして落とし穴のマスは踏破にカウントされない……マジかよ。

  • 急いで69階のK2000を回収する。
  • 81階トレジャーでモルモットソードなるものを見つけたので、モルモット状態のネコに装備する。大ダメージが出せた。それは良いけど、モルモットが治らない。
  • ダメージは固定値で287496。……何だこの半端な値は。数字6桁を見たらマス目だと思え、でいいのだろうか。
  • 28, 74, 96というマスは存在しない。
  • 69, 47, 82のマスはあるけど特に何も起きなかった。
  • 70階71階を完全踏破。
  • 行方不明4人とモルモット3人を除外すると、使えるキャラが少ないので、次に全滅するとヤバい。だいぶヤバい。
  • 万が一に備えてジョラスをレベル43→51まで育成。

52時間:84階まで、完全踏破61階、最高レベル80

52〜54時間

  • 68階のルエンゾを回収する。
  • 行方不明4人、モルモット3人、今使えるレベル60以上は7人。7人のうちゲイシャブランとントレヤナはステータス低いから育成したくないんだよなぁ。
  • 80階にどうせドラゴンがいるんでしょ、レベル80なんだから……見つけた。80, 42, 45で、あれ? 行方不明になってたシュローツェだった。
  • 80±1, 62, 76 に誰かいることを突き止めて79階へ。行方不明になってたフラウだった。
  • 今使えるレベル60以上は9人。これならまぁ安心。
  • K2000(ロボ)のEPを上げて、高エナジーミサイルを装備可能にした。こいつ、いわゆるぶっ壊れ性能してるな。
  • 85階で落とし穴を見た。この(80〜89階)地帯にも落とし穴ってあるんだ……。
  • 86階でテレポーテーションを手に入れた。「床が無い場合は失敗となりパーティーメンバー全員がバラバラに飛ばされます」割と怖いこと書いてあるな……
  • テレポーテーションがあった座標はイベントブックで見られるから、そこを指定して86階からスタートできるのね。
  • あとはレア系のショップにもすぐ行ける。というわけで謎の液体の店にワープして買い込んできた。
  • しかしテレポーテーションのコストが高いから、アビリティポイントを増やしておきたい。
  • 63階・62階を踏破。

54時間:86階まで、完全踏破64階、最高レベル80

54〜56時間

  • しかしここから先をどう進めば良いんだろう。武器はずっとサジタリウスの弓矢で固定で、それより強いのは落ちてこないし。
  • 万が一に備えてモデナリ(レベル47)を連れて、踏破階数を増やしに。64階を踏破。
  • 65階で地図問題の答えに気づいた。ネコ音波。しかしいまネコはモルモット状態で使えないのや……
  • 65階を踏破。モデナリも52まで育ちました。
  • この辺にドラゴンいるんでしょ……と言いつつ81階を探索していたら、行方不明だったオウユーを発見。
  • もう4人パーティーでも、アンクで0階に戻る→パーティー人数減らしてテレポーテーション→回収して戻る になるからかなり楽だね。
  • 86階から進んでみるも、88階あたりで限界を感じる。戦闘をいくつも重ねるのが難しい。
  • 91階で1回か2回戦闘するだけなら倒せるんじゃないの? と、やってみる。
    • K2000の面目躍如。高エナジーミサイルが強すぎる。
    • ここでガストラフェテスを敵が落としていった! 別格最強武器やん。

56時間:88階まで、完全踏破66階、最高レベル86

56〜58時間

  • 88階から仮想階段で89階に降りる。
    • 仮想エレベーターで下に床が無かったときの挙動ってどうなるんだろう 落ち続けて行方不明になるのか?
    • →「床はない」と出て移動しないだけになる。良かった
  • 89階から帰還成功。
  • そしてFFマスを見つけた。(これ99階にあるのかと思ったら89階にあるのか……!)
  • ここより手前で十分強化してから挑んだほうが良いんだろうなぁ。
  • 82階を踏破。
  • 全滅しました(行方不明)。6回目。
    • 87階あたりで、行方不明者までのマス数を見ながら歩いてたら落とし穴(見えてた)に落ちた。マジかよ。
  • 行方不明者までのマス数で混乱しないように、最初の方の行方不明者を回収しとくか。
  • 16, 29, 33 のイルベルトを回収。ついでに2階にずっといたロードピークを回収。
  • 39階で銃を買うついでに行方不明者の位置を特定。 39+44, 73, 37 ラウラギ。(5回目で落とし穴に落ちた人)
  • 行方不明者は(6回目で)落とし穴に落ちた4人と、バルトロとエバレスの6人。
  • モデナリ(レベル52)を連れつつ行方不明者を探して回ることにする。
  • 57〜59階が割と広範囲にマス目が分布している・移動しやすい。このへんを歩きながら行方不明者までの距離を見て、95階にオウユーがいることを特定。
  • 95階にアイテム「ゲーム」を売る店がある……なんじゃこりゃ。
  • 99, 58, 36 エルバ を発見、回収。91階にフラウを発見、回収。
  • 再び59階へ。
  • 91階でルエンゾを発見。
  • 行方不明者の状況は以下の通り。
    • (6回目で)落とし穴に落ちた4人のうち2人回収完了。95階オウユーと91階ルエンゾは発見済みで未回収。
    • 78階でバルトロが石化してる、発見まで完了
    • 最後のエバレスは「行方不明」だけどレーダーに探知されないみたいね。どうなってるんだ。
  • 一方通行式転送装置の2と3を見つけた。これ何の意味があるの?

58時間:89階まで、完全踏破67階、最高レベル87

58〜60時間

  • 90階、ちょっと様子を見てみるか……絶対ボスを倒さなきゃいけないってことは、一本道の途中にいるみたいな形かな?
  • 90階は変な形状のマス目配置をしている。
  • 敵と戦わないように歩いていたつもりが、気づかないうちにFEマスを踏んだらしい。
  • 傀儡のエバレス→パノプティコアに勝利。スタッフロールが流れた。すごい呆気ないというか拍子抜けというか。ガストラフェテスもアドレスブレイドも装備してなかったし、他の装備も適当だったのに。
  • 90階以降のマスが視認しにくいのが全部悪い。
  • 73階を踏破。

60時間:

60〜62時間

  • (6回目で)落とし穴に落ちた4人のうち、残してた95階オウユーと91階ルエンゾを回収。
  • 全滅しました。7回目。
    • 97階も戦闘1回くらいだったら行けるかなーと(一方通行式転送装置のマスにテレポーテーションして)F4マスを踏んだらベテルギウス×5が出てきて一気に死んだ。あんなん勝てるの?
  • この場合は、再度テレポーテーションして、やられたマスに移動して、キャラを回収すればよい。簡単に4人とも回収できた。
  • 87階を踏破。
  • 88階、やたらとモンスターのマスが多くないか?
  • 78階を踏破、バルトロを回収。
  • 74, 75, 76階を踏破。

58時間:95階まで、完全踏破76階、最高レベル92

62〜64時間

  • 久しぶり(今日は2022年1月30日)過ぎて状況を覚えてない……
  • メンバー転送を持ってないからいちいち厄介なのよね。70階台にあるってブログを見た気がする。残りは72階か。
  • 72階でメンバー転送を取得。そして完全踏破。
  • 24〜26階をまだ踏破してなかったので完全踏破。
  • 91階をウロウロしてるけど、メリュジーヌが出現するマスが多くてうかつに戦闘できない。
  • 287496 = 66 ^ 3 らしい。けど66, 66, 66というマスは無い。どうしろと?

64時間:95階まで、完全踏80階、最高レベル94

  • モルモットを治す方法が分からんので考える。
  • 45階にテレポーテーションする。
  • モルモットソードの与えるダメージが300763に変わった。何で? と思ったら300763 = 67 ^ 3である。
  • 「モルモットソードを装備した人のレベルの3乗」が与えるダメージになるんだろう。
  • あり得るとしたら魔法反射をつけて→メリュジーヌが呪文を唱えたら反射する? かなぁ。と思ったけど、違うようだ。
  • 分からんのでネタバレを見た。
  • https://twitter.com/kiryuda/status/1451923841740013568
  • 一度わかればあとは簡単、モルモットの4人の治癒が完了した。
  • 91〜95階あたりならまぁ戦えるようになってきた。が、なかなかアイテムは落ちない。
  • 91階、95階を完全踏破。
  • 88階も踏破……しようとしたけど敵が多くて鬱陶しい……
  • レベル66のネコを連れて行って、これを育てよう。

64〜66時間


  • 武器(物理ダメージ)
    • 最強1つ手前
      • 単体固定ダメージ:カラドボルグ ← 鉄巨人 ← C6、C8 ← 〜階
      • 全体固定ダメージ:リグナムシャフト ← BF、C1 ←
      • 単体ランダムダメージ:
      • 単体固定ダメージ(遠隔):サジタリウスの弓矢 ← インプ ← B0、B3 ← 〜階
      • 全体ランダムダメージ(遠隔):ガラビ銃 ← 諜報員 ← BC、BE ← 〜階
  • 防具
    • 最強1つ手前
      • グランドヘルム ← ドラゴンゴースト ← C0、C3 ← 〜階

74階 B8〜BC 75階 BB〜BF 76階 BD〜C1 77階 C0〜C3

79階 C4〜C7

66〜時間

  • 久しぶり(今日は2022年12月7日)過ぎて状況を覚えてない……
  • 適当に死んだりしたけど、テレポーテーションとメンバー転送を組み合わせると一瞬で全員救出できるから楽になった。
  • 90階を完全踏破。あれこれアビリティポイントが 3じゃなくて5増えるのな。
  • 謎の液体をたくさん入手して、HPと装備ポイントをどんどん上げれば良いっぽい?
  • 98階に足を踏み入れた。これ00階とほとんど同じという構造なのかー。

68時間:99階まで?、完全踏87階、最高レベル98

pandasのappendができない? もとのDataFrameは変更されないので、返り値を使う

pandasのappendができない? もとのDataFrameは変更されないので、返り値を使う

pandasのappend関数を使うときに、たまに間違えて失敗するのでメモ。

pandasでDataFrameにappendするとき、連結後のDataFrameは返り値になっている。もとのDataFrameは変更されない。

以下、詳細。

準備

import pandas as pd
import numpy as np
pd.options.display.notebook_repr_html = False  # jupyter notebook上での出力形式を制御するために書いています。無くても動きます。
# 動作環境の確認
print(pd.__version__)
print(np.__version__)

# --------------------

1.1.2
1.19.1

Python標準のappend

pythonの普通のリストはappendがインプレースに行われる。

公式ドキュメント:5. データ構造 — Python 3.10.0b2 ドキュメント

my_list = [1, 3, 5]
my_list

# --------------------

[1, 3, 5]
my_list.append(7)
my_list

# --------------------

[1, 3, 5, 7]

このとき、append関数の返り値はNoneである。

ret = my_list.append(9)
print(ret)

# --------------------

None

pandasのappend

pandasではリストはappendがインプレースに行われない。返り値を取ってこなきゃいけない。
これはPython標準のappendとは違う動きなので、混同しないように注意する必要がある。

公式ドキュメント:pandas.DataFrame.append — pandas 1.4.1 documentation
仕様はあまりハッキリ書いていないが、 Returns(返り値)が新しいDataFrameであることに注意。

df = pd.DataFrame({'col_A': [1,2,3], 'col_B': ['p','q','r']})
df

# --------------------

   col_A col_B
0      1     p
1      2     q
2      3     r
df2 = pd.DataFrame({'col_A': [100, 200], 'col_B': ['x', 'y']})
df2

# --------------------

   col_A col_B
0    100     x
1    200     y
# 返り値は結合後のDataFrameとなる
df.append(df2)

# --------------------

   col_A col_B
0      1     p
1      2     q
2      3     r
0    100     x
1    200     y
# もとのDataFrameであるdfは変わっていない
df

# --------------------

   col_A col_B
0      1     p
1      2     q
2      3     r

appendにはinplace引数もない。
ので、dfを新しくしようと思ったら、結果を代入する必要がある。

df = df.append(df2)
df

# --------------------

   col_A col_B
0      1     p
1      2     q
2      3     r
0    100     x
1    200     y

特に、ループの中でうっかり間違えてappendを書くと、ループを抜けても何も追加されていないということが起きる。
(以下の書き方はDataFrameを1行ずつ追加していくものであり、動作が遅くなるのであまり良い方法ではない。説明用の例である)

# ループの中でappendする例
# 
df = pd.DataFrame()
for i in range(5):
    temp = {
        'num': i,
        'square': i**2,
        'cubic': i**3
    }
    df.append(temp, ignore_index=True)
    # appendの結果を代入していないので、dfはループの中で変わらない
# dfは空のDataFrameである
df

# --------------------

Empty DataFrame
Columns: []
Index: []
df = pd.DataFrame()
for i in range(5):
    temp = {
        'num': i,
        'square': i**2,
        'cubic': i**3
    }
    df = df.append(temp, ignore_index=True)
df

# --------------------

   cubic  num  square
0    0.0  0.0     0.0
1    1.0  1.0     1.0
2    8.0  2.0     4.0
3   27.0  3.0     9.0
4   64.0  4.0    16.0

おまけ:numpyのappend関数

NumPyは公式ドキュメントにこの動作が明示的に説明してあるから良いですね。

Note that append does not occur in-place: a new array is allocated and filled.
(拙訳: appendはインプレースではないことに注意してください。新しいNumPy配列が確保されて、そこに値が埋められます。)
https://numpy.org/doc/stable/reference/generated/numpy.append.html

NumPyのappend関数の動きはpandasと同様だ。すなわち、appendの動作はインプレースではなく、追加したあとのNumPy配列を使うには返り値を見る必要がある。

numpy_array = np.array([1, 3, 5])
numpy_array

# --------------------

array([1, 3, 5])
# 返り値は結合後のNumPy配列となる
other = np.array([2, 4])
np.append(numpy_array, other)

# --------------------

array([1, 3, 5, 2, 4])
# もとのNumPy配列であるnumpy_arrayは変わっていない
numpy_array

# --------------------

array([1, 3, 5])

それでは。

4回目の緊急事態宣言と東京都・大阪府の人出の変化を可視化してみた

はてなではここ数週間、Knoa氏の匿名ダイアリーによる感染者数予測が耳目を集めている(何だこの矛盾に満ちた表現)。

あんな予測は俺にはできないなと思いつつ、5chのコロナ関連のスレッドをウロウロしていたら、 【公式】モバイル空間統計 | 位置情報などのビッグデータを利用した人口統計情報というデータを見つけたので、ちょっと可視化してみた。予測や分析じゃなくてただの可視化です。
出典は「NTTドコモ モバイル空間統計」である。

データについての注意点

  • 全国主要エリアの15時時点の人口増減率である。(夜間の人口については何もわからないことに注意)
  • 「緊急事態宣言前」に対する比率である。明確な説明は無いが、おそらく2020/4/7の人口に対する比率だと思われる。(基準はコロナ前ではなく、すでにコロナの影響がある日であることに注意。)
  • 「15時の時点でそのエリアにいた具体的な人数」はグラフになっているのでデータはあるはずだが、csvには記載されていない。つらい。
  • 各地点の人口の推移グラフは、サイトトップからたどるのが少し難しいが、緊急事態宣言前後の人口変動分析 などにある。

グラフ表示についての注意点

  • 横軸に日付、縦軸には2020/4/7 (火曜日)を100としたときの各エリアの人口の値を表示している。
  • 背景の赤色は「緊急事態宣言」の期間、黄色は「蔓延防止等重点措置」の期間を示す。
    • 4回目の緊急事態宣言 7/12 〜 8/22 は、とりあえず7月いっぱいを赤色にしている
  • 平日は濃紺(navy)、休日は赤色(red)で表示。
  • 平日と休日の判定は「土曜日もしくは日曜日であるか」のみである。(すなわち、祝日や年末年始は全く考慮していない)

取り急ぎでやってみたので上記の通り少し粗がある。本当は感染者数の推移と重ね合わせるのもやりたいんだけど。
【公式】モバイル空間統計 | 位置情報などのビッグデータを利用した人口統計情報は全国のいろいろな箇所の統計を取ってるけど、ここで見るのは基本的に東京都の地点のデータである。最後にちょっとだけ大阪を見てみます。

東京都 新宿駅

f:id:soratokimitonoaidani:20210722150100p:plain

グラフ背景に色を付けてみると、東京都に緊急事態が出ている期間って長すぎるだろ! 殆ど出てるだろ! と改めて思ってしまった。 それはさておき、グラフを見ると以下のことが分かる。

  • 2回目、3回目の緊急事態宣言の期間中は、平日と休日とも「人口が一度減って、その後増えていく」という傾向を示している。
  • 4回目の緊急事態宣言、7月12日〜で人口は殆ど減少していないように見える。少なくとも2回めや3回目と比べて、減少幅は少ないように見える。
  • 7月17・18日の週末(4回目の緊急事態宣言が出てから最初の週末)は、6月の緊急事態宣言時と比べて人口が多い。

渋谷センター街

f:id:soratokimitonoaidani:20210722150121p:plain

  • 2回目、3回目の緊急事態宣言の期間中は、平日と休日とも「一度減って、その後増えていく」という傾向を示している。
  • 4月1日前後を極大値として、4月に入ると急激に人出が減少する。何でだろう? 感染者が増えたことで外出を控えたか、単に春休みが終わったか、あたりだろうか。
  • 4回目の緊急事態宣言、7月12日〜で人口は殆ど減少していないように見える。少なくとも2回めや3回目と比べて、減少幅は少ないように見える。
  • 7月17・18日の週末(4回目の緊急事態宣言が出てから最初の週末)は、6月の緊急事態宣言時と比べて人口が多い。

東京駅

f:id:soratokimitonoaidani:20210722150137p:plain

平日に多いパターンも見たほうが良いかと思い、東京駅を見てみた。
平日の中で一日だけ数値が減少しているのは祝日ですね……(例えば2月11日の建国記念の日、2月23日の天皇誕生日)可能なら本当は色を変えたほうが良いと思う。

新宿や渋谷センター街と同様の傾向ですね。

  • 2回目、3回目の緊急事態宣言の期間中は、平日と休日とも「人口が一度減って、その後増えていく」という傾向を示している。
  • 4回目の緊急事態宣言、7月12日〜で人口は殆ど減少していないように見える。少なくとも2回めや3回目と比べて、減少幅は少ないように見える。
  • 7月17・18日の週末(4回目の緊急事態宣言が出てから最初の週末)は、6月の緊急事態宣言時と比べて人口が多い。

霞が関

f:id:soratokimitonoaidani:20210722150149p:plain

東京駅とそれほど傾向は変わらない。ので箇条書きは省略。
5月19日(灰色縦線で示したところ)だけが前後の平日よりも顕著に低い。
霞が関の各省庁でテレワークの実体を調査した5月19日だけ、人流がガクンと減り、翌日はまた増えている」という事実が確認できる。下記の記事を参照。

霞が関、テレワーク調査日だけ人出減 事前通知「明日だけは」 | 毎日新聞

羽田空港 第1ターミナル

f:id:soratokimitonoaidani:20210722150201p:plain

  • 2回目の緊急事態宣言の期間中は、はじめに減少していって2月前半が極小値になっている。
    • 他の地点と比べて極小値の時期が遅いのは、飛行機に乗るのはチケットを取ってから乗るまでにタイムラグがあるから、そのせい?
  • 3回目の緊急事態宣言の期間中は、5月のゴールデンウィークが終わったあたりが極小値になっている。
  • ここで一番注目したいのは、2021年7月21日が今年の(ほぼ)最大値になっていることである。
    • 7月22日〜25日の4連休の前に休みを取って、空港を利用した ≒ 旅行に出かけた人が一定数いる? 東京から他の地方なのか、他の地方から東京なのかは不明だが、東京以外の感染拡大がこの後発生してもおかしくないと思う。

大阪府 梅田

最後に大阪を1枚だけ。東京都とはかなり様子が異なる。

f:id:soratokimitonoaidani:20210722150219p:plain

  • 6月20日の前後で全く人口の様子が違う。
    • 6月20日の前 は、平日 > 土日
    • 6月20日の後 は、平日 < 土日
  • 大阪は「3回目の緊急事態宣言が出ている間は、土日に自粛して外出を控えた」と推察される。
  • そして「3回目の緊急事態宣言が解除された6月20日以降、一気に土日に外出するようになった」と推察される。
    • 大阪府では7月11日頃から感染者数が急激に上昇している。
  • 東京ではこのような「緊急事態宣言の期間中は低いまま、緊急事態宣言の終了を境に一気に人口が増える」という例は見つからず、大阪に特徴的である(大阪府の難波の人口も、かなり近い傾向を示している)。
  • なお、6月に入ってから平日の人口が急に上がるのは、平日の商業施設などの休業要請を6月1日から解除したからだと思われる。下記参照。

一方、現在、百貨店などの大規模施設に出している休業要請は、土日に限っての要請に切り替え、平日については、夜8時までの時短要請を行います。 https://www3.nhk.or.jp/news/special/coronavirus/emergency_third/kansai.html#mokuji0

感想

考察というほどちゃんとした話ではないので、「感想」ってセクション名にしておきますが。 少なくとも、「緊急事態宣言が出たので外出を控えます」とか「感染者が増えて危ないので外出を控えます」といった行動の変化は、東京都ではほとんど発生していないと考えられる。 (自粛している人は、緊急事態宣言や感染者の数によらず前から自粛している)

一方で、東京の感染者数を5週間ぶん予測した (6月21日版) で参考として示されている都内主要繁華街における滞留人口モニタリング(7月18日までのデータ) | 公益財団法人 東京都医学総合研究所を見ると、 東京の繁華街の人口は7月12日の緊急事態宣言以降、明らかな減少傾向にあるように見える。(時間帯別主要繁華街滞留人口の日別推移 というグラフを参照)
特に、上記グラフの基準時刻である15時を含む、昼間でも減少している。
東京都モニタリング会議でも都内主要繁華街における滞留人口モニタリング(7月18日までのデータ) | 公益財団法人 東京都医学総合研究所の分析をもとに「宣言発令後の直近1週間で、レジャー目的の繁華街滞留人口は 減少」と結論づけている。((第55回)東京都新型コロナウイルス感染症モニタリング会議資料(令和3年7月21日)|東京都防災ホームページ
上で見てきた傾向と違うのは何故だろうか。
「人口のカウントの仕方の違い」か「エリアの違い」のいずれかだと思うが、よく分からない。

もし追実験したいという奇特な方がいましたら、適当に書いたnotebookを以下に置いてあります。参考にしてください。
fragments_for_blog/mobile_space_stat.ipynb at master · Linus-MK/fragments_for_blog · GitHub

新型コロナウイルスに関する記事だと、昔こんな記事も書きました。

linus-mk.hatenablog.com

それでは。