折れ線かドットか

きしださんのツイートで紹介された高校教科書の「高校生のインターネット利用率の国際比較」というグラフについて、伊藤先生荻原さんボブさんボブさんがコメントされた。これはいつも問題になる「名義尺度を折れ線グラフでつないでいいのか」という問題である。「レーダーチャートの開き」のようなものなので問題ないという意見もある一方で、レーダーチャートについても並び順で面積が変わることをMiwaさん襖屋さんが指摘された。

まずは元の図を、ここでは縦軸と横軸を逆にして、Pythonで描いてみる。

import matplotlib.pyplot as plt

countries = ["オランダ", "イタリア", "シンガポール", "韓国", "日本", "トルコ"]
home   = [98.9, 97.4, 95.4, 83.5, 81.4, 68.3]
school = [93.9, 66.5, 69.7, 42.7, 59.7, 49.2]

fig, ax = plt.subplots(figsize=(6.4, 4.0))
ax.plot(home,   countries, marker="o", linestyle="-", label="自宅での利用")
ax.plot(school, countries, marker="s", linestyle="-", label="学校での利用")
ax.invert_yaxis()
ax.set_xlim(0, 100)
ax.set_xlabel("利用率(%)")
ax.set_title("高校生のインターネット利用率(2012年)")
ax.grid(True, alpha=0.3)
ax.legend()
plt.tight_layout()
高校生のインターネット利用率(2012年)

Clevelandもこのような図が嫌いだったらしく、ドットプロット(dot plot)と称して、線を除いた描き方を提案している。線を除けばこんな感じになる。

import matplotlib.pyplot as plt

countries = ["オランダ", "イタリア", "シンガポール", "韓国", "日本", "トルコ"]
home   = [98.9, 97.4, 95.4, 83.5, 81.4, 68.3]
school = [93.9, 66.5, 69.7, 42.7, 59.7, 49.2]

fig, ax = plt.subplots(figsize=(6.4, 4.0))
ax.scatter(home,   countries, marker="$H$", label="自宅での利用")
ax.scatter(school, countries, marker="$S$", label="学校での利用")
ax.invert_yaxis()
ax.set_xlim(0, 100)
ax.set_xlabel("利用率(%)")
ax.set_title("高校生のインターネット利用率(2012年)")
ax.grid(True, alpha=0.3)
ax.legend()
plt.tight_layout()
高校生のインターネット利用率(2012年)

マーカーを日本語の文字にするのは少し厄介だ(GPT-4.2に教えてもらった。もっと良い方法があったら教えてください):

import matplotlib.pyplot as plt
from matplotlib.textpath import TextPath
from matplotlib.transforms import Affine2D

def text_marker(ch, size=1, prop=None):
    """
    文字 ch を TextPath でパス化し、中心が(0,0)になるように平行移動した Path を返す
    size は “元の字形サイズ” で、表示サイズは plot の markersize で調整するのが楽
    """
    tp = TextPath((0, 0), ch, size=size, prop=prop)
    bb = tp.get_extents()
    cx, cy = (bb.x0 + bb.x1) / 2, (bb.y0 + bb.y1) / 2
    return tp.transformed(Affine2D().translate(-cx, -cy))

countries = ["オランダ", "イタリア", "シンガポール", "韓国", "日本", "トルコ"]
home   = [98.9, 97.4, 95.4, 83.5, 81.4, 68.3]
school = [93.9, 66.5, 69.7, 42.7, 59.7, 49.2]

y = range(len(countries))
m_home   = text_marker("自")
m_school = text_marker("学")

fig, ax = plt.subplots(figsize=(6.4, 4.0))

ax.plot(home,   countries, linestyle="None", marker=m_home,
        markersize=12, markeredgecolor="none", label="自宅での利用")
ax.plot(school, countries, linestyle="None", marker=m_school,
        markersize=12, markeredgecolor="none", label="学校での利用")

ax.set_yticks(y)
ax.set_yticklabels(countries)
ax.invert_yaxis()
ax.set_xlim(0, 100)
ax.set_xlabel("利用率(%)")
ax.grid(True, alpha=0.3)
ax.legend()
plt.tight_layout()
plt.show()
高校生のインターネット利用率(2012年)

ボブさんのご指摘のように、2変数なら散布図にする方が賢そうだ。

import matplotlib.pyplot as plt

countries = ["オランダ", "イタリア", "シンガポール", "韓国", "日本", "トルコ"]
home   = [98.9, 97.4, 95.4, 83.5, 81.4, 68.3]
school = [93.9, 66.5, 69.7, 42.7, 59.7, 49.2]

fig, ax = plt.subplots(figsize=(6.4, 6.4))

ax.scatter(home, school, s=60)

for x, y, name in zip(home, school, countries):
    ax.text(x + 1.0, y + 1.0, name, fontsize=10, ha="left", va="bottom")

ax.plot([0, 100], [0, 100], linestyle="--", linewidth=1)

ax.set_xlim(0, 100)
ax.set_ylim(0, 100)
ax.set_xlabel("自宅での利用率(%)")
ax.set_ylabel("学校での利用率(%)")
ax.set_title("高校生のインターネット利用率(2012年)")
ax.grid(True, alpha=0.3)
ax.set_aspect("equal", adjustable="box")  # 横縦のスケールを揃える
plt.tight_layout()
plt.show()
高校生のインターネット利用率(2012年)