はじめに
「アンケートの結果を、パッと見やすいグラフにしたい」
「いつもExcelやGoogleスプレッドシートでグラフを作成しているが、手間がかかって大変」
こんなこと、一度は思ったことがあるのではないでしょうか。
先日、私たちが運営している「教育現場の生成AIなんでも相談所」コミュニティで、中学校で美術を教えているA先生からこんなご相談をいただきました。
近日中に授業の公開研究会(授業・研究協議)を予定しているのですが、授業の前後で生徒向けアンケートを実施しています。今までは、公開研究会が終わってから、事前事後の結果の変容グラフをExcelで作っていました。
そこで、今回は生成AIを使い、授業後の休み時間10分で事後アンケートのデータを読み込んでグラフを作成してみたいと思っています。そうすれば、直後にある研究協議でグラフを見せられるので、とても便利です。 事前・事後アンケートデータから、瞬時にその変容を可視化するプロンプトを知りたいです!
果たして、そんな最強のプロンプトは作れるのでしょうか?
目次
- はじめに
- 目次
- 実際にプロンプトを開発してみた
- ①生成AIにプロンプトを作ってもらう
- ②プロンプトをコピペ
- 見えてきた課題
- ①現場での再現性
- ②グラフの正確性
- ③機密性
- まとめ
- みなさんも、お悩み相談してみませんか?
実際にプロンプトを開発してみた
頂いたお悩みをもとに、コミュニティ内のワークショップで、他の教育関係者の方々と協力しながら、事前・事後アンケートの変容を瞬時にグラフ化するプロンプトを模索しました。
実際に、A先生から頂いたアンケートデータ(生徒の個人情報は抜いたもの)を用いて、ChatGPTでプロンプトを開発してみた様子を紹介します。
①生成AIにプロンプトを作ってもらう
1からコードを開発するのは大変なので、こちらから条件を提示して、より精密なプロンプトを生成AIに開発してもらいます。
グラフ生成の際は、日本語の文字化けが起こりやすいので注意が必要です。今回は、制約条件に「文字化けが起こらないように工夫」せよ、と指示をしました。
✅プロンプト例はこちら↓
あなたは優秀なデータアナリストです。 次の二つのデータ事前と事後を変容として参考として積み上げグラフにまとめてください。 ##制約条件 - できている、どちらかといえばできている、どちらかといえばできてない、できていないなど4つの回答の割合の変化を積み上げグラフにしてください。 - 回答の色は、選択肢ごとに統一し、グラデーションになるようにしてください。 - 質問項目(1)~(3)ごとに、事前事後を比較 - 日本語が文字化けしないように工夫 - 見やすくわかりやすいグラフを作成 - コピペできる生成AIプロンプトを作成 - タイムアウトせずにグラフ処理が完了できるように工夫 ##重要 プレゼンテーション本番で使用するため、以下の条件で確実に動作するグラフを作成してください: - シンプルで確実に動作するコード - ブラウザ互換性を重視した実装 - 複雑な機能は避け、基本的な積み上げ棒グラフに集中 - エラーが発生しないよう十分テストされた構造 - 全てのグラフを1枚のpngで出力 - 全ての元データの回答を反映 - 質問文はグラフに載せなくて大丈夫です。質問番号だけ表示してください。 - 元データとグラフの数値が合致しているか確認する工程も設けてください。 - 汎用性が高くなるように、プロンプトを書き直してください。
すると、コピペ用のプロンプトを出力してくれました。↓
##coding: utf-8 -
""" 目的:
- 4カテゴリ(できている/どちらかといえばできている/どちらかといえばできていない/できていない)を 事前・事後で比較する100%積み上げ棒(Q1〜Q3)
- 質問文は非表示、軸ラベルは Q1〜Q3 × 事前/事後 のみ
- 日本語フォント自動(文字化け回避)、matplotlibのみ
- 可視化と同一計算で検証表(人数・割合)をCSV出力
I/O:
- 入力: Excel 2ファイル(先頭シート)。最初の「文字列列」3つをQ1〜Q3として扱う
- 出力: PNG 1枚, 検証CSV 1枚 """
import pandas as pd, numpy as np, matplotlib.pyplot as plt import matplotlib.font_manager as fm import unicodedata, os, re
##===== 設定 =====
PRE_XLS = "/mnt/data/事前 (回答).xlsx" # ←差し替え可 POST_XLS = "/mnt/data/事後 (回答).xlsx" # ←差し替え可 PNG_PATH = "/mnt/data/pre_post_stacked_Q1_Q3.png" CSV_PATH = "/mnt/data/pre_post_stacked_Q1_Q3_values.csv"
##日本語フォント(自動選択)
font_candidates = [ "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", "/usr/share/fonts/opentype/noto/NotoSansCJKjp-Regular.otf", "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", "/usr/share/fonts/truetype/ipafont-gothic/ipagp.ttf", "/usr/share/fonts/opentype/ipafont-mincho/ipam.ttf", "/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc", "/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc", ] fp = next((p for p in font_candidates if os.path.exists(p)), None) plt.rcParams["font.family"] = fm.FontProperties(fname=fp).get_name() if fp else "sans-serif" plt.rcParams["axes.unicode_minus"] = False
##読み込み(先頭シート)
pre = pd.read_excel(PRE_XLS, sheet_name=0) post = pd.read_excel(POST_XLS, sheet_name=0)
##対象列(Q1〜Q3)=文字列型の先頭3列
def pick_q_cols(df): return [c for c in df.columns if df[c].dtype == "O"][:3] pre_cols, post_cols = pick_q_cols(pre), pick_q_cols(post)
##カテゴリと表記ゆれ吸収
CATS = ["できていない","どちらかといえばできていない","どちらかといえばできている","できている"] ALIASES = { "出来ていない":"できていない", "出来ている":"できている", "どちらかと言えばできていない":"どちらかといえばできていない", "どちらかと言えば できていない":"どちらかといえばできていない", "どちらかと言えばできている":"どちらかといえばできている", "どちらかと言えば できている":"どちらかといえばできている", "どちらかといえば、できている":"どちらかといえばできている", "どちらかといえば、できていない":"どちらかといえばできていない", } def norm(x): if not isinstance(x, str): return np.nan t = unicodedata.normalize("NFKC", x).strip().strip(" .,。・;;::") return ALIASES.get(t, t)
def counts_pcts(df, col): vc = df[col].map(norm).value_counts(dropna=True) total = int(vc.sum()) cnts = [int(vc.get(c, 0)) for c in CATS] pcts = [(v/total*100.0 if total>0 else 0.0) for v in cnts] return cnts, pcts
##データ構築(Q1〜Q3 × 事前/事後)
labels, bars, rows = [], [], [] for i in range(3): pre_c = pre_cols[i] if i < len(pre_cols) else None post_c = post_cols[i] if i < len(post_cols) else None for phase, col in [("事前", pre_c), ("事後", post_c)]: labels.append(f"Q{i+1}\n{phase}") if col is None: bars.append([0,0,0,0]); continue cnts, pcts = counts_pcts(pre if phase=="事前" else post, col) bars.append(pcts) for ci, cat in enumerate(CATS): rows.append({"Question":f"Q{i+1}","Phase":phase,"Category":cat,"Count":cnts[ci],"Percent (plot)":round(pcts[ci],2)})
data = np.array(bars).T
##描画
fig, ax = plt.subplots(figsize=(12,6)) colors = {"できていない":"#6b7280","どちらかといえばできていない":"#a3a3a3","どちらかといえばできている":"#facc15","できている":"#f59e0b"} x = np.arange(len(labels)); bottom = np.zeros(len(labels)) for i, cat in enumerate(CATS): ax.bar(x, data[i], bottom=bottom, label=cat, width=0.64, edgecolor="white", linewidth=0.8, color=colors[cat]) bottom += data[i] ax.set_xticks(x); ax.set_xticklabels(labels, fontsize=11) ax.set_ylim(0,100); ax.set_yticks(np.arange(0,101,10)); ax.set_ylabel("割合(%)", fontsize=12) ax.set_title("事前・事後 比較(Q1〜Q3)— 4カテゴリの100%積み上げ", fontsize=14, pad=12) leg = ax.legend(title="回答カテゴリ", loc="upper center", bbox_to_anchor=(0.5,-0.12), ncol=2, frameon=False, fontsize=11) if leg and leg.get_title(): leg.get_title().set_fontsize(11) for j in range(len(labels)): y0=0 for i, cat in enumerate(CATS): h=data[i,j] if h>=7: ax.text(j, y0+h/2, f"{h:.0f}%", ha="center", va="center", fontsize=10) y0+=h ax.grid(axis="y", linestyle="--", alpha=0.4) plt.tight_layout() plt.savefig(PNG_PATH, dpi=200, bbox_inches="tight")
##検証CSV
pd.DataFrame(rows).to_csv(CSV_PATH, index=False, encoding="utf-8-sig") print("Saved:", PNG_PATH, CSV_PATH)
②プロンプトをコピペ
次に、新しいチャットを開き、事前・事後データを添付し、①で出力されたプロンプトをコピペして入力します。
すると、以下のようなグラフが出力されました。
できました! • Download the PNG • Download the CSV 検証表 気になる点があれば、そのまま上のスクリプトに追記・修正します(例:凡例位置、色、表示したい値、列名の自動検証など)。
とても見やすいグラフになっていますね。
(グラフを直したい場合は、修正箇所を明確に指摘し、生成AIとやり取りしながらプロンプトを書き直してもらいます。ちなみに、私は5回ほどやり取りしてプロンプトを作り直しました。)
しかし、これで十分かと思いきや、色々と問題点が浮かび上がってきました。
見えてきた課題
①現場での再現性
ChatGPTやClaudeの無料版を使うと、すぐにデータ制限に達してしまいました。また、有料版を使っても、ファイルのデータ量の多さやグラフ処理に時間がかかり、頻繁にタイムアウトが起こり、思ったようにグラフの出力がうまくいきません。データやグラフ処理には結構な時間を要する場合が多いようです。
②グラフの正確性
さらに、グラフで表された数値が元データと合致していないケースもありました。グラフを見てみると、合計人数が合っていなかったり、項目が入れ替わっていたりすることも。最終的には、人間の目で元データとの整合性を確認することが必要そうです。
③機密性
機密性の面では、入力データに氏名などの個人情報が含まれていると、それらが生成AIモデルの学習に使われる可能性があります。一度学習データに取り込まれると、将来的に生成結果として再現・出力される恐れがあるので、個人情報の管理はとても重要です。
今回用いたデータは、生徒の個人情報を含めないことを徹底していました。また、生成AIの設定を変更し、入力データの学習機能をオフにすることもできます。
しかし、個人情報や機密情報の有無のチェックや、データからそれらを取り除く手間がかかります。誤って個人情報を含んだデータを渡してしまった場合、取り消しも効きません。入念に確認し、慎重に扱う必要があります。
このような課題を踏まえると、実際に教育現場でこのプロンプトを利用するにはまだまだハードルが高く、意外と手間がかかることが伺えます。また、他の教員の方々にも将来的に使っていただくとなると、より使いやすく、安心・安全に活用できることを示す必要があるように感じます。
これらの点を考慮して、A先生は、今回も従来通りExcelを用いてグラフを作成することにしたそうです。
まとめ
今回は、事前・事後アンケートの変容を瞬時にグラフ化したい!というA先生が、実際に使えるプロンプトを開発してはみたものの、現場での再現性や正確性、機密性を考慮した結果、今回は生成AIを使わないことにした、という事例を取り上げました。
このように、生成AIを活用する姿勢を持つと同時に、それがベストな手段なのかを目的やリスクに応じてその都度判断することが大切です。
実際に生成AIを活用していく中で、できることとその限界を学び、臨機応変に対応していくことが重要になってくるのではないでしょうか?
みなさんも、お悩み相談してみませんか?
本記事は、Manabi AI 開発チームがSlackで運営するコミュニティ「教育現場の生成AIなんでも相談所」で寄せられた相談をもとに作成しました。
このコミュニティには、国内外から350名以上の教育関係者が参加し、教育現場における生成AIの導入や活用について日々情報交換をしています。月1回のオンライン相談会では、現場の悩みをコミュニティメンバーと一緒に考えることもできます。
教育×生成AIに関心のある方は、ぜひ以下のフォームからご登録ください。 https://forms.gle/WuBvxBoDfU8VXEao9