Google Play データ活用

Google Playレビュー可視化入門|日次・月次の評価推移をPlotlyで分析する方法

2025年10月11日

この記事では、Google Playレビューを使って、レビュー件数や評価スコアの推移をPlotlyで可視化する方法を解説します。

日次・月次のレビュー件数、平均スコア、星評価の構成比、アプリバージョン別の評価傾向をグラフ化することで、アップデートやイベント後にユーザー評価がどのように変化したかを確認しやすくなります。

本記事では、前回の記事で取得したGoogle PlayレビューCSVを使い、分析しやすい形に集計してからPlotlyで可視化します。

この記事でできること

  • Google PlayレビューCSVを読み込んで日付列を整形する
  • 日次・月次のレビュー件数を集計する
  • 平均スコアの推移を確認する
  • 星評価の構成比を100%積み上げグラフで可視化する
  • アプリバージョン別にレビュー件数や評価傾向を見る
  • レビュー件数・平均スコア・星評価構成比を組み合わせて評価変動を読み解く

想定読者

  • Google PlayレビューをPythonで可視化したい方
  • レビュー件数や評価スコアの推移を時系列で確認したい方
  • アップデート前後で評価が変化したかを見たい方
  • Plotlyでインタラクティブなグラフを作りたい方
  • レビュー分析記事やダッシュボードの素材を作りたい方

事前準備

この記事では、Google Playレビューを取得済みで、CSVファイルとして保存されている前提で進めます。レビュー取得がまだの場合は、先に以下の記事を確認してください。

Google Playのデータ取得方法まとめ|google-play-scraperでアプリ情報・レビューを取得する

今回使う主な列は以下です。

列名内容
contentレビュー本文
score星評価
atレビュー投稿日
reviewCreatedVersionレビュー投稿時のアプリバージョン
thumbsUpCount参考になった数

過去のコードや取得方法によっては、バージョン列が appVersion という名前になっている場合があります。本記事では、基本的に reviewCreatedVersion を使う想定で整理します。

使用ライブラリ

可視化には、pandasとPlotlyを使います。

pip install -U pandas plotly

Plotlyは、拡大・縮小、ホバー表示、凡例のON/OFFなどができるインタラクティブなグラフを作れるライブラリです。時系列グラフや構成比グラフを確認する際に便利です。

CSVを読み込んで日付列を整える

まず、取得済みのレビューCSVを読み込みます。ここでは、前回記事で保存した reviews_genshin_paged.csv を例にします。

import pandas as pd

csv_path = "reviews_genshin_paged.csv"

df = pd.read_csv(csv_path)

df["at"] = pd.to_datetime(df["at"], errors="coerce")
df = df.dropna(subset=["at", "score"]).copy()

df["date"] = df["at"].dt.date
df["month"] = df["at"].dt.to_period("M").dt.to_timestamp()

print(df[["content", "score", "at", "date", "month"]].head())
print("rows:", len(df))

at はレビュー投稿日です。日次集計には date、月次集計には month を使います。

レビュー件数と平均スコアの推移を可視化する

まずは、レビュー件数と平均スコアの推移を可視化します。レビュー件数はユーザー反応の量、平均スコアは評価の方向性を見るための基本指標です。

レビュー件数が増えた時期に平均スコアも変化している場合、アップデート、イベント、不具合、キャンペーンなどの影響が出ている可能性があります。

日次では細かい変化を確認しやすく、月次では長期的な傾向を把握しやすくなります。

import plotly.graph_objects as go

daily_summary = (
    df.groupby("date")
      .agg(
          review_count=("score", "size"),
          avg_score=("score", "mean"),
      )
      .reset_index()
)

monthly_summary = (
    df.groupby("month")
      .agg(
          review_count=("score", "size"),
          avg_score=("score", "mean"),
      )
      .reset_index()
)

fig_trend = go.Figure()

fig_trend.add_trace(go.Bar(
    x=daily_summary["date"],
    y=daily_summary["review_count"],
    name="日次レビュー件数",
    yaxis="y1",
    visible=True,
))

fig_trend.add_trace(go.Scatter(
    x=daily_summary["date"],
    y=daily_summary["avg_score"],
    name="日次平均スコア",
    mode="lines+markers",
    yaxis="y2",
    visible=True,
))

fig_trend.add_trace(go.Bar(
    x=monthly_summary["month"],
    y=monthly_summary["review_count"],
    name="月次レビュー件数",
    yaxis="y1",
    visible=False,
))

fig_trend.add_trace(go.Scatter(
    x=monthly_summary["month"],
    y=monthly_summary["avg_score"],
    name="月次平均スコア",
    mode="lines+markers",
    yaxis="y2",
    visible=False,
))

fig_trend.update_layout(
    title="レビュー件数と平均スコア推移(日次・月次切り替え)",
    xaxis_title="日付 / 月",
    yaxis=dict(
        title="レビュー件数",
        side="left",
    ),
    yaxis2=dict(
        title="平均スコア",
        overlaying="y",
        side="right",
        range=[1, 5],
    ),
    template="plotly_white",
    height=700,
    updatemenus=[
        {
            "buttons": [
                {
                    "label": "日次",
                    "method": "update",
                    "args": [
                        {"visible": [True, True, False, False]},
                        {"title": "レビュー件数と平均スコア推移(日次)"},
                    ],
                },
                {
                    "label": "月次",
                    "method": "update",
                    "args": [
                        {"visible": [False, False, True, True]},
                        {"title": "レビュー件数と平均スコア推移(月次)"},
                    ],
                },
            ],
            "direction": "right",
            "x": 0.5,
            "xanchor": "center",
            "y": 1.15,
            "yanchor": "top",
        }
    ],
)

fig_trend.show()

fig_trend.write_html(
    "plot_review_trend_switch.html",
    include_plotlyjs="cdn",
    full_html=True,
)

レビュー件数が少ない日は、1件のレビューで平均スコアが大きく変動します。そのため、日次グラフでは細かな変化を見つつ、月次グラフで長期傾向を確認するのがおすすめです。

レビュー件数と平均スコア推移(日次・月次切り替え)

星評価の構成比を100%積み上げで可視化する

平均スコアだけでは、低評価と高評価の内訳が見えにくい場合があります。そこで、星1〜星5の構成比を100%積み上げ棒グラフで可視化します。

たとえば、平均スコアが同じでも、星3が多い安定型なのか、星1と星5に分かれる賛否両論型なのかで、読み取り方は変わります。

def make_score_ratio(df: pd.DataFrame, period_col: str) -> pd.DataFrame:
    score_counts = (
        df.groupby([period_col, "score"])
          .size()
          .unstack(fill_value=0)
    )

    for score in [1, 2, 3, 4, 5]:
        if score not in score_counts.columns:
            score_counts[score] = 0

    score_counts = score_counts[[1, 2, 3, 4, 5]]

    score_ratio = score_counts.div(score_counts.sum(axis=1), axis=0) * 100
    return score_ratio.reset_index()


daily_ratio = make_score_ratio(df, "date")
monthly_ratio = make_score_ratio(df, "month")

fig_ratio = go.Figure()

for score in [1, 2, 3, 4, 5]:
    fig_ratio.add_trace(go.Bar(
        x=daily_ratio["date"],
        y=daily_ratio[score],
        name=f"★{score}",
        visible=True,
    ))

for score in [1, 2, 3, 4, 5]:
    fig_ratio.add_trace(go.Bar(
        x=monthly_ratio["month"],
        y=monthly_ratio[score],
        name=f"★{score}",
        visible=False,
    ))

fig_ratio.update_layout(
    barmode="stack",
    title="星評価構成比(日次・月次切り替え)",
    xaxis_title="日付 / 月",
    yaxis_title="構成比(%)",
    yaxis=dict(range=[0, 100]),
    template="plotly_white",
    height=600,
    updatemenus=[
        {
            "buttons": [
                {
                    "label": "日次",
                    "method": "update",
                    "args": [
                        {"visible": [True] * 5 + [False] * 5},
                        {"title": "星評価構成比(日次)"},
                    ],
                },
                {
                    "label": "月次",
                    "method": "update",
                    "args": [
                        {"visible": [False] * 5 + [True] * 5},
                        {"title": "星評価構成比(月次)"},
                    ],
                },
            ],
            "direction": "right",
            "x": 0.5,
            "xanchor": "center",
            "y": 1.15,
            "yanchor": "top",
        }
    ],
)

fig_ratio.show()

fig_ratio.write_html(
    "plot_score_ratio_switch.html",
    include_plotlyjs="cdn",
    full_html=True,
)
星評価構成比(日次・月次切り替え)

バージョン別のレビュー件数を可視化する

Google Playレビューには、レビュー投稿時のアプリバージョンが含まれる場合があります。バージョン別にレビュー件数を見ることで、特定バージョンでレビューが増えていないかを確認できます。

まず、バージョン列を整えます。取得データによっては reviewCreatedVersion ではなく appVersion という列名になっている場合があるため、どちらにも対応できるようにします。

version_col = None

for candidate in ["reviewCreatedVersion", "appVersion"]:
    if candidate in df.columns:
        version_col = candidate
        break

if version_col is None:
    raise ValueError("アプリバージョン列が見つかりません。")

df["version"] = df[version_col].fillna("不明")

version_counts = (
    df.groupby("version")
      .size()
      .reset_index(name="review_count")
      .sort_values("review_count", ascending=False)
)

print(version_counts.head(20))

バージョンが空のレビューは、ここでは「不明」として扱っています。古いレビューや投稿環境によっては、バージョン情報が入っていない場合があります。

import plotly.express as px

top_versions = version_counts.head(20)["version"].tolist()
df_ver = df[df["version"].isin(top_versions)].copy()

ver_daily = (
    df_ver.groupby(["date", "version"])
          .size()
          .reset_index(name="review_count")
)

fig_ver_count = px.line(
    ver_daily,
    x="date",
    y="review_count",
    color="version",
    title="バージョン別レビュー件数推移",
    labels={
        "date": "日付",
        "review_count": "レビュー件数",
        "version": "アプリバージョン",
    },
    height=600,
)

fig_ver_count.update_layout(template="plotly_white")

fig_ver_count.show()

fig_ver_count.write_html(
    "plot_version_count.html",
    include_plotlyjs="cdn",
    full_html=True,
)
バージョン別レビュー件数推移

バージョン別の星評価構成比を見る

次に、バージョン別に星評価の構成比を確認します。特定バージョンで低評価が増えている場合、不具合、仕様変更、バランス調整、UI変更などが影響している可能性があります。

score_ver = (
    df_ver.groupby(["version", "score"])
          .size()
          .unstack(fill_value=0)
)

for score in [1, 2, 3, 4, 5]:
    if score not in score_ver.columns:
        score_ver[score] = 0

score_ver = score_ver[[1, 2, 3, 4, 5]]

score_ver_ratio = score_ver.div(score_ver.sum(axis=1), axis=0) * 100
score_ver_ratio = score_ver_ratio.reset_index()

fig_ver_ratio = px.bar(
    score_ver_ratio,
    x="version",
    y=[1, 2, 3, 4, 5],
    title="バージョン別 星評価構成比",
    labels={
        "value": "構成比(%)",
        "version": "アプリバージョン",
        "variable": "星評価",
    },
    barmode="stack",
    height=600,
)

fig_ver_ratio.update_layout(
    template="plotly_white",
    yaxis=dict(range=[0, 100]),
    xaxis_tickangle=-45,
)

fig_ver_ratio.show()

fig_ver_ratio.write_html(
    "plot_version_ratio.html",
    include_plotlyjs="cdn",
    full_html=True,
)
バージョン別 星評価構成比

可視化結果を見るときの注意点

Google Playレビューの可視化では、以下の点に注意が必要です。

  • レビューは取得時点のスナップショットであり、過去履歴を完全に再現するものではありません。
  • レビュー件数が少ない日は、平均スコアが大きくブレやすくなります。
  • アプリバージョンが空のレビューは、バージョン別分析では「不明」として扱うか、除外する必要があります。
  • 低評価が増えた理由は、レビュー本文やアップデート履歴とあわせて確認する必要があります。
  • 平均スコアだけでなく、星評価の構成比やレビュー件数もセットで見ると解釈しやすくなります。

特に、レビュー件数が少ない期間では、数件のレビューだけで平均スコアが大きく上下します。日次と月次を切り替えながら、細かい変化と長期傾向を分けて見ることが重要です。

次に読む記事

まとめ

この記事では、Google PlayレビューをPlotlyで可視化し、レビュー件数、平均スコア、星評価構成比、アプリバージョン別の推移を見る方法を紹介しました。

レビュー件数はユーザー反応の量、平均スコアは評価の方向性、星評価構成比は評価の内訳を確認するのに役立ちます。これらを日次・月次で切り替えながら見ることで、アップデートやイベント後の変化を把握しやすくなります。

可視化だけでは理由までは分からないため、次はレビュー本文のテキスト分析や感情分析、トピック抽出と組み合わせて、評価変動の背景を読み解いていきます。

-Google Play, データ活用