🧞

AutoGenでデータ分析してみる

2024/04/12に公開

やりたいこと

AutoGenというマルチエージェントがワイワイして勝手に話を進めてくれるエージェントのフレームワークをMicrosoftで作成されているとのことで、データ分析業務に活用できないか試します。

AutoGenの公式ドキュメント

https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat/

ここに説明がありますが、要約すると

ConversableAgentという親クラスがいて、サブクラスに3種類のエージェントがいるみたいで、役割とできることが違うみたいです。

タスク例としては、こんなパターンが紹介されています。

ここにもう少し詳細の例とNotebookがあります。
https://microsoft.github.io/autogen/docs/Examples#automated-multi-agent-chat

今回は下記を選んでみました。

Automated Data Visualization by Group Chat (with 3 group member agents and 1 manager agent)

https://microsoft.github.io/autogen/docs/notebooks/agentchat_groupchat_vis

とりあえず、書かれているままにNotebookで実行すると、下記のように
User_proxyとCoder、Criticの3人がエラー出したり、コード書いたり、実行したりして進んでいきます。

実行結果

User_proxy (to chat_manager):

download data from https://raw.githubusercontent.com/uwdata/draco/master/data/cars.csv and plot a visualization that tells us about the relationship between weight and horsepower. Save the plot to a file. Print the fields in a dataset before visualizing it.


Coder (to chat_manager):

To complete this task, I will provide a Python script that will perform the following steps:

  1. Download the dataset from the given URL.
  2. Read the CSV data into a Pandas DataFrame.
  3. Print the fields of the dataset to the console.
  4. Plot a scatter plot to visualize the relationship between weight and horsepower.
  5. Save the plot to a file.

Please run the following script:

# filename: download_and_plot.py

import pandas as pd
import matplotlib.pyplot as plt

# Step 1: Download the dataset
url = "https://raw.githubusercontent.com/uwdata/draco/master/data/cars.csv"
df = pd.read_csv(url)

# Step 2: Read the CSV data into a Pandas DataFrame (already done in Step 1)

# Step 3: Print the fields of the dataset
print("Fields in the dataset:")
print(df.columns.tolist())

# Step 4: Plot a scatter plot to visualize the relationship between weight and horsepower
plt.figure(figsize=(10, 6))
plt.scatter(df['Weight_in_lbs'], df['Horsepower'], alpha=0.5)
plt.title('Relationship between Weight and Horsepower')
plt.xlabel('Weight (lbs)')
plt.ylabel('Horsepower')
plt.grid(True)

# Step 5: Save the plot to a file
plt.savefig('weight_vs_horsepower.png')
plt.close()  # Close the plotting window

print("Plot saved to 'weight_vs_horsepower.png'.")

After you have saved the code to download_and_plot.py, run this script with Python, and it will perform all the tasks outlined above. Make sure you have Python, Pandas, and Matplotlib installed in your environment before running the script.


EXECUTING CODE BLOCK 0 (inferred language is python)...
User_proxy (to chat_manager):

exitcode: 1 (execution failed)

なんかよくわからないけど、やりとりをしてくれていて、コードを実行しつつエラーに対応してるっぽいです。
最終的な結果がよくわからなかったので、違うパターンを試します。

エージェントの定義はそのままで、指示データを変えてみます。

user_proxy = autogen.UserProxyAgent(
    name="User_proxy",
    system_message="A human admin.",
    code_execution_config={
        "last_n_messages": 3,
        "work_dir": "groupchat",
        "use_docker": False,
    },  # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.
    human_input_mode="NEVER",
)
coder = autogen.AssistantAgent(
    name="Coder",  # the default assistant agent is capable of solving problems with code
    llm_config=llm_config,
)
critic = autogen.AssistantAgent(
    name="Critic",
    system_message="""Critic. You are a helpful assistant highly skilled in evaluating the quality of a given visualization code by providing a score from 1 (bad) - 10 (good) while providing clear rationale. YOU MUST CONSIDER VISUALIZATION BEST PRACTICES for each evaluation. Specifically, you can carefully evaluate the code across the following dimensions
- bugs (bugs):  are there bugs, logic errors, syntax error or typos? Are there any reasons why the code may fail to compile? How should it be fixed? If ANY bug exists, the bug score MUST be less than 5.
- Data transformation (transformation): Is the data transformed appropriately for the visualization type? E.g., is the dataset appropriated filtered, aggregated, or grouped  if needed? If a date field is used, is the date field first converted to a date object etc?
- Goal compliance (compliance): how well the code meets the specified visualization goals?
- Visualization type (type): CONSIDERING BEST PRACTICES, is the visualization type appropriate for the data and intent? Is there a visualization type that would be more effective in conveying insights? If a different visualization type is more appropriate, the score MUST BE LESS THAN 5.
- Data encoding (encoding): Is the data encoded appropriately for the visualization type?
- aesthetics (aesthetics): Are the aesthetics of the visualization appropriate for the visualization type and the data?

YOU MUST PROVIDE A SCORE for each of the above dimensions.
{bugs: 0, transformation: 0, compliance: 0, type: 0, encoding: 0, aesthetics: 0}
Do not suggest code.
Finally, based on the critique above, suggest a concrete list of actions that the coder should take to improve the code.
""",
    llm_config=llm_config,
)

groupchat = autogen.GroupChat(agents=[user_proxy, coder, critic], messages=[], max_round=20)
manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=llm_config)

SHIBUYA OPEN DATAのバスのハチ公ルートの乗降客数のデータから「春の小川」の1日平均利用者数を
出してとだけ変えてみました。

https://city-shibuya-data.opendata.arcgis.com/datasets/b5bce58c9c074a7eb2163fe099aaa79f_0/explore

user_proxy.initiate_chat(
    manager,
    message="download data from https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_月別ルート別ハチ公バス乗降客数/FeatureServer/replicafilescache/131130_月別ルート別ハチ公バス乗降客数_-3496582268428418369.csv and plot a visualization that tells us about the relationship between 春の小川 and 1日平均利用者数. Save the plot to a file. Print the fields in a dataset before visualizing it.",
)

同様に3者のやりとりが行われて、下記のコードが生成されたっぽいです。

# filename: plot_passenger_data.py
import pandas as pd
import requests
import matplotlib.pyplot as plt
from io import StringIO

# Retrieve and read the CSV data
csv_url = 'https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_月別ルート別ハチ公バス乗降客数/FeatureServer/replicafilescache/131130_月別ルート別ハチ公バス乗降客数_-3496582268428418369.csv'
response = requests.get(csv_url)
csv_text = response.content.decode('utf-8')
data = pd.read_csv(StringIO(csv_text))

# Filter data for 春の小川 (assuming 'ルート' is the field that contains route names)
haru_no_ogawa_data = data[data['ルート'] == '春の小川']

# Plot 春の小川 against 1日平均利用者数
plt.figure(figsize=(10, 5))
plt.scatter(haru_no_ogawa_data['年月'], haru_no_ogawa_data['1日平均利用者数'], color='blue', alpha=0.5)
plt.title('Average Daily Passengers for Haru no Ogawa route')
plt.xlabel('Date')
plt.ylabel('Average Daily Passengers')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('haru_no_ogawa_passengers.png')  # Save the plot to a file

# Show the plot (comment this line if running headless)
plt.show()

まあまあ、わかるなーという図が出てきました。
2020年に落ち込んで、2023年に2019年と同等かそれ以上の平均利用人数になってそうだなと。

日本語とかで定義しても大丈夫なのかなーと思ってやってみました。

user_proxy.initiate_chat(
    manager,
    message="https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_月別ルート別ハチ公バス乗降客数/FeatureServer/replicafilescache/131130_月別ルート別ハチ公バス乗降客数_-3496582268428418369.csv からデータをダウンロードして 夕やけこやけ の 1日平均利用者数をヒストグラムで出して"
)

日本語でコメントとかも書いてくれていましたが、途中でCriticが英語のやりとりに戻してしまいました。

実行結果

User_proxy (to chat_manager):

https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_月別ルート別ハチ公バス乗降客数/FeatureServer/replicafilescache/131130_月別ルート別ハチ公バス乗降客数_-3496582268428418369.csv からデータをダウンロードして 夕やけこやけ の 1日平均利用者数をヒストグラムで出して


Coder (to chat_manager):

まず、ユーザーの指示に従って、指定されたURLからCSVデータをダウンロードする必要があります。次に、Pythonを使用してデータを読み込み、「夕やけこやけ」のルートに関するデータをフィルタリングし、1日平均利用者数をヒストグラムとして表示するコードを提供します。以下のPythonコードを実行してください。

# filename: download_and_plot.py
import pandas as pd
import matplotlib.pyplot as plt
import requests
from io import StringIO

# データをダウンロードするためのURL
url = "https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_月別ルート別ハチ公バス乗降客数/FeatureServer/replicafilescache/131130_月別ルート別ハチ公バス乗降客数_-3496582268428418369.csv"

# データをダウンロードする
response = requests.get(url)
response.encoding = response.apparent_encoding  # 文字コードを設定

# HTTPステータスコードが200(OK)の場合のみデータを読み込む
if response.status_code == 200:
    # CSVデータを読み込む
    data = pd.read_csv(StringIO(response.text))
    # '夕やけこやけ'ルートのデータをフィルタリング
    filtered_data = data[data['ルート'] == '夕やけこやけ']
    # 1日平均利用者数を取得
    users_per_day = filtered_data['平均利用者数']

    # データが存在する場合のみヒストグラムを表示
    if not users_per_day.empty:
        plt.hist(users_per_day, bins=20, color='blue', edgecolor='black')
        plt.title('夕やけこやけ 1日平均利用者数のヒストグラム')
        plt.xlabel('1日平均利用者数')
        plt.ylabel('出現頻度')
        plt.grid(True)
        plt.show()
    else:
        print("指定されたルート '夕やけこやけ' のデータはありません。")
else:
    print(f"データのダウンロードに失敗しました。ステータスコード: {response.status_code}")

このスクリプトは、指定されたURLからデータをダウンロードし、'夕やけこやけ'のルートについての1日平均利用者数のヒストグラムを作成し、表示します。スクリプトを保存したら、Pythonを介して実行してください。何かエラーが表示された場合は、私にお知らせください。


Critic (to chat_manager):

Since direct execution or validation of the code is not possible, here is the critique based on the provided code snippet for the requested visualization task:

  • Bugs (3/10): The code appears to be logically correct, with no syntax errors visible in the provided snippet, assuming that the CSV structure from the URL matches the expected format. However, the code is likely to fail if the HTTP request does not succeed or if the CSV file has a different structure than expected (e.g., if it does not contain the 'ルート' or '平均利用者数' columns).

そこでCriticをDatascientistにして、日本語にしてみます。

user_proxy = autogen.UserProxyAgent(
    name="User_proxy",
    system_message="A human admin.",
    code_execution_config={
        "last_n_messages": 3,
        "work_dir": "groupchat",
        "use_docker": False,
    },  # Please set use_docker=True if docker is available to run the generated code. Using docker is safer than running the generated code directly.
    human_input_mode="NEVER",
)
coder = autogen.AssistantAgent(
    name="Coder",  # the default assistant agent is capable of solving problems with code
    llm_config=llm_config,
)
critic = autogen.AssistantAgent(
    name="Datascientist",
    system_message="""データサイエンティスト。あなたは与えられたデータ分析コードの品質を評価することに長けた有用なアシスタントです。1(悪い)から10(良い)のスケールでスコアを提供し、明確な根拠を提示しながら評価を行います。各評価において、データ分析のベストプラクティスを考慮しなければなりません。具体的には、以下の次元に沿ってコードを慎重に評価します。
バグ(bugs):バグ、論理エラー、構文エラー、またはタイポは存在しますか?コードがコンパイルに失敗する理由はありますか?どのように修正すべきですか?任意のバグが存在する場合、バグスコアは5未満でなければなりません。
データ変換(transformation):データは分析タイプに適切に変換されていますか?例えば、データセットは適切にフィルタリング、集約、またはグループ化されていますか?日付フィールドが使用される場合、そのフィールドは最初に日付オブジェクトに変換されていますか?
目標遵守(compliance):コードは指定された分析目標をどの程度満たしていますか?
分析タイプ(type):ベストプラクティスを考慮した上で、分析タイプはデータと意図に適していますか?より効果的な洞察を伝える分析タイプはありますか?異なる分析タイプがより適切である場合、スコアは5未満でなければなりません。
データエンコーディング(encoding):データは分析タイプに適切にエンコードされていますか?
美学(aesthetics):分析の美学は、分析タイプとデータに適していますか?

上記の各次元に対してスコアを提供する必要があります。
{bugs: 0, transformation: 0, compliance: 0, type: 0, encoding: 0, aesthetics: 0}

コードの提案は行わないでください。

最後に、上記の批評に基づいて、コーダーがコードを改善するために実行すべき具体的なアクションリストを提案してください。
""",
    llm_config=llm_config,
)

groupchat = autogen.GroupChat(agents=[user_proxy, coder, critic], messages=[], max_round=20)
manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=llm_config)

少し指示を具体的にしますが、データ自体は年間の平均利用者数はないので、そこは空気を読んで欲しいところです。

user_proxy.initiate_chat(
    manager,
    message="https://services3.arcgis.com/UtdeFTavkHfI94t2/arcgis/rest/services/131130_月別ルート別ハチ公バス乗降客数/FeatureServer/replicafilescache/131130_月別ルート別ハチ公バス乗降客数_-3496582268428418369.csv からデータをダウンロードして 「夕やけこやけ」 ルートの年ごとの平均利用者数をヒストグラムで出して"
)

データサイエンティストは日本語で説明してくれるし、ちゃんとヒストグラムができたみたいです。

実行結果

Datascientist (to chat_manager):

エラーメッセージから、キーエラーが発生していることがわかります。これは、スクリプトが 'ルート名' という列をデータフレーム内で見つけることができないことを意味します。CSVファイルが正しくダウンロードされていることが確認できましたが、列名が異なっているか、存在しない何らかの理由が考えられます。

ここで私が実行できる評価は以下の通りです:

バグ(bugs): 1/10
エラーメッセージによると、指定された列名がデータフレームに存在しないため、実行時にエラーが発生しています。列名がデータと一致していることを事前に確認する必要がありました。

データ変換(transformation): 0/10
スクリプトはデータの変換に関連する部分に到達していないため評価できません。

目標遵守(compliance): 1/10
指定された分析目標は、バグが修正されない限りは達成できません。

分析タイプ(type): 0/10
指定された分析を実行するためには、まずバグを修正し、コードを機能させる必要があります。

データエンコーディング(encoding): 0/10
データの読み込みに失敗しているため、エンコーディングについて評価することはできません。

美学(aesthetics): 0/10
ヒストグラムが生成されていないため、この点では評価できません。

改善のためのアクションリスト:

  • CSVファイルを確認し、正しい列名を特定してください。
  • 列名をスクリプト内で正しいものに修正してください。
  • エラーハンドリングを導入して、不正な列名やその他の潜在的なエラーに対処できるようにします。
  • スクリプトがデータを正しく読み込んで変換した後、ヒストグラムを表示して視覚化の美学を評価できるようにします。

EXECUTING CODE BLOCK 0 (inferred language is python)...
User_proxy (to chat_manager):

exitcode: 1 (execution failed)
Code output:
Traceback (most recent call last):
line 3805, in get_loc
return self._engine.get_loc(casted_key)
File "index.pyx", line 167, in pandas._libs.index.IndexEngine.get_loc
File "index.pyx", line 196, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/hashtable_class_helper.pxi", line 7081, in pandas._libs.hashtable.PyObjectHashTable.get_item
File "pandas/_libs/hashtable_class_helper.pxi", line 7089, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 'ルート名'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "plot_histogram.py", line 9, in <module>
yuyake_data = df[df['ルート名'] == '夕やけこやけ']
line 3812, in get_loc
raise KeyError(key) from err
KeyError: 'ルート名'


Coder (to chat_manager):

キーエラーの発生原因である列名 'ルート名' がCSVファイル内に存在しないことが明らかになったので、CSVファイルのヘッダーを確認して正しい列名を見つける必要があります。

次のステップでは、ダウンロードしたCSVファイルの最初の数行を読み込んで列名を確認するPythonスクリプトを提供します。このステップを行うと、'ルート名' の代わりに使用すべき正しい列名を決定できます。

以下のPythonスクリプトを実行して、CSVファイルのヘッダー部分を確認してください。

# filename: check_csv_header.py
import pandas as pd

# CSVファイルを読み込む
df = pd.read_csv('bus_data.csv', encoding='utf-8')

# 最初の5行を印刷して列名を確認
print(df.head())

このスクリプトはCSVファイルを読み込み、先頭の5行を表示します。これにより、列名を確認してから次のステップに進めます。スクリプトが実行された後、出力された内容から正しい列名を見つけてください。


EXECUTING CODE BLOCK 0 (inferred language is python)...
User_proxy (to chat_manager):

exitcode: 0 (execution succeeded)
Code output:
ルート 年度_元号 年月 年 月 月間利用者数 1日平均利用者数 ObjectId
0 神宮の杜 令和3年度 202104 2021 4 27477 915.9 1
1 神宮の杜 平成30年度 201804 2018 4 66714 2223.8 2
2 丘を越えて 令和3年度 202109 2021 9 30109 1003.6 3
3 夕やけこやけ 平成30年度 201903 2019 3 21675 699.2 4
4 丘を越えて 平成31・令和1年度 202002 2020 2 31203 1114.4 5


Datascientist (to chat_manager):

出力内容に基づいて、'ルート名' ではなく 'ルート' が正しい列名であることがわかります。これで、プログラマーはスクリプトを修正し、特定のルートに対する年ごとの平均利用者数の計算とヒストグラムの表示を進めることができます。

以下に、修正すべき点を指摘します:

バグ(bugs): 9/10
実行時の問題は、正しい列名 'ルート' を使用することで修正できます。その他のスクリプトの部分は機能する可能性があります。

データ変換(transformation): 0/10
本来のコードは正しいデータ変換を意図していたと考えられますが、実行に至っていないため評価はできません。

目標遵守(compliance): 0/10
修正されてないバグにより目標を遵守することはできませんでしたが、列名を修正することで期待される出力が得られる可能性があります。

分析タイプ(type): 0/10
分析タイプは適切ですが、バグのために結果を生成できませんでした。

データエンコーディング(encoding): 0/10
適切なデータエンコーディングは、バグ修正後に確認可能です。

美学(aesthetics): 0/10
ヒストグラムはまだ生成されていないので評価することはできません。

改善のためのアクションリスト:

  1. スクリプトの 'ルート名' を 'ルート' に修正する。
  2. 日付変換などの適切な行の処理ができるように、その他のスクリプト部分を再度確認する。
  3. 再度スクリプトを実行し、目的に応じた図がプロットされることを確認する。
  4. 得られた図について美学を評価し、分析結果を文脈に沿って解釈する。

これらのステップに続いて、プログラマーはスクリプトを実行し直して、期待されるヒストグラムを生成できるはずです。


Coder (to chat_manager):

修正すべき箇所が特定できましたので、コードの 'ルート名' を正しい 'ルート' に置き換え、再度実行するスクリプトを紹介します。また、年月の分割方法もCSVファイルの形式に合わせて修正します。

修正を反映したスクリプトを以下に示します。このコードは、"夕やけこやけ" ルートの年ごとの平均利用者数を計算してヒストグラムを表示します。

# filename: plot_histogram_corrected.py
import pandas as pd
import matplotlib.pyplot as plt

# CSVファイルの読み込み
df = pd.read_csv('bus_data.csv', encoding='utf-8')

# "夕やけこやけ"ルートに該当するデータだけを抽出
yuyake_data = df[df['ルート'] == '夕やけこやけ']

# '年'列を使って年別にグループ化し、月間利用者数の平均を計算
annual_average = yuyake_data.groupby('年')['月間利用者数'].mean()

# 年別の平均利用者数のヒストグラムをプロット
annual_average.plot(kind='bar')
plt.title('年ごとの "夕やけこやけ" ルートの平均利用者数')
plt.xlabel('年')
plt.ylabel('平均利用者数')
plt.tight_layout()  # 追加: タイトルやラベルが適切に表示されるようにする
plt.show()

このスクリプトを実行してください。'夕やけこやけ' ルートの年ごとの平均利用者数のヒストグラムが正しく表示されるはずです。


EXECUTING CODE BLOCK 0 (inferred language is python)...
User_proxy (to chat_manager):

exitcode: 0 (execution succeeded)
Code output:
Figure(640x480)


Datascientist (to chat_manager):

スクリプトが無事実行され、ヒストグラムが生成されたようです。出力がテキストとして「Figure(640x480)」と表示されていますが、これはヒストグラムのグラフィカルな出力を意味します。このグラフを実際に見ることができないのは、こちらがテキストベースの環境であるためです。

最終的な評価を次のように提供します。

バグ(bugs): 9/10
最初の問題は修正されたようですが、確認のための出力が見えないため、この評価は暫定的なものです。

データ変換(transformation): 8/10
修正されたコードはデータ変換を適切に処理するはずですが、実際のデータを見て確認することができません。

目標遵守(compliance): 9/10
提供された修正されたコードは、目標とされた分析を達成するために必要な手順を踏むようです。

分析タイプ(type): 9/10
ヒストグラムは与えられた目標に対して適切な分析タイプです。

データエンコーディング(encoding): 9/10
他の部分が期待通りに動作すると仮定すると、データエンコーディングは適切であると言えます。

美学(aesthetics): 8/10
コードの plt.tight_layout() の追加はグラフの美学を向上させる良いプラクティスですが、グラフ自体を見ることができないため、この評価は暫定的です。

改善のためのアクションリスト:

  1. プロットが正しく形成されていることを確認するために、GUI環境でスクリプトを実行し、グラフの表示を直接確認してください。
  2. データが意図した通りに正しく集計されているか確認するために、生成されたヒストグラムの数値や軸のラベルを検証してください。
  3. ヒストグラムに利用者数の年ごとの変動が正確に示されているか、データソースと照らし合わせて確認してください。

コードが期待通りに機能し、適切に美的なヒストグラムが表示されていれば、分析は成功です。また、将来的な分析において、コードの再利用や改善がしやすいよう、ドキュメント化や関数化を行うことも検討してください。


コードを実行すると文字化けしていますが、確かに年度ごとの利用者数平均のヒストグラムが出ていそうです。

指示が曖昧だった(年間の利用者数平均ってなに?)ので、年間の利用者数合計を出すとかもあるかと思いましたが、月間利用者数の平均を年度ごとにだしてくれているようで、下記のロジックも正しそうです。

# '年'列を使って年別にグループ化し、月間利用者数の平均を計算
annual_average = yuyake_data.groupby('年')['月間利用者数'].mean()

まとめ

データ分析と言っても、ファイルのダウンロードから可視化までを実施しただけですが、
複数人のやりとりで考えをブラッシュアップしていくのは、いろいろな観点でデータを見て示唆を出す、
データアナリストの業務を自動化できる可能性が高いなーと感じました。

他のAutoGenのタスクも試してみようと思います。

ヘッドウォータース

Discussion