ChatGPTにジュリア集合を描画するPythonコードを書いてもらった

ChatGPTにジュリア集合を描画するPythonコードを書いてもらいました。

 

指示文(プロンプト):

ジュリア集合を描画するPythonプログラムを提示してください。できるだけ高速に描画するコードにしてください。

 

回答:

Pythonプログラムを提示します。 このプログラムは、複素数 c = -0.8 + 0.156i を使ってジュリア集合を描画します。実行すると、以下のような画像が出力されます。高速に描画するためには、numpyやnumbaというライブラリを使って、計算を最適化する必要があります。numpyは、数値計算を効率的に行うためのライブラリです。numbaは、Pythonのコードを高速化するためのライブラリです。


# ライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
import numba # numbaをインポート
import colorsys

# グラフのサイズと解像度
plt.figure(figsize=(6, 6), dpi=100)

# 複素平面の範囲と刻み幅
re_min, re_max = -2, 2
im_min, im_max = -2, 2
re_step = (re_max - re_min) / 1000
im_step = (im_max - im_min) / 1000

# 複素数cの設定
a = -0.8
b = 0.156
c = complex(a, b)

# ジュリア集合を描画する関数(numbaで高速化)
@numba.njit # numbaのデコレータを付ける
def julia(re, im):
    z = complex(re, im) # 初期値z0
    n = 0 # 繰り返し回数
    while abs(z) < 2 and n < 100: # zが発散しないかぎり繰り返す
        z = z**2 + c # 漸化式
        n += 1 # 回数をカウント
    return n # 回数を返す

# 色の設定(HSV色空間)
hsv_tuples = [(x*1.0/100, 0.5, 0.5) for x in range(100)]
rgb_tuples = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))

# 複素平面上の点に対してjulia関数を適用して色を付ける(numpyで高速化)
re_array = np.arange(re_min, re_max, re_step) # 実部の配列
im_array = np.arange(im_min, im_max, im_step) # 虚部の配列
n_array = np.zeros((len(re_array), len(im_array))) # 繰り返し回数の配列
for i in range(len(re_array)):
    for j in range(len(im_array)):
        n_array[i][j] = julia(re_array[i], im_array[j]) # 繰り返し回数を取得

# グラフの表示(matplotlibで高速化)
plt.imshow(n_array.T, cmap="hsv", extent=[re_min, re_max, im_min, im_max], origin="lower") # 配列を画像として表示
plt.axis("off") # 軸を非表示にする
plt.show() # グラフを表示する
    
結果:

以下のように成功しました。最初に提示されたコードには「import colorsys」が抜けていてエラーになったので、そこだけ手作業で追加してます。

ChatGPTにQRコードを生成するPythonコードを書いてもらった

ChatGPTに任意の文字列(URLとか)を埋め込んだQRコードを生成するPythonコードを書いてもらいました。

 

指示文(プロンプト):

指定した文字列からQRコード画像を生成するPythonのコードを書いてください。データと生成するQRコードの画像名は、標準入力から引数として渡せるようにしてください。

 

回答:

以下は、Pythonのqrcodeライブラリを使用して指定した文字列からQRコード画像を生成する基本的なコードです。


import sys
import qrcode

def generate_qr_code(data, file_name):
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )
    qr.add_data(data)
    qr.make(fit=True)
    
    img = qr.make_image(fill_color="black", back_color="white")
    img.save(file_name)

# コマンドライン引数からデータとファイル名を取得
data = sys.argv[1]
file_name = sys.argv[2]
generate_qr_code(data, file_name)

    

上記のコードでは、generate_qr_code関数を定義し、dataとfile_nameを引数として受け取ります。dataはQRコードエンコードする文字列です。

file_nameは生成されたQRコード画像の保存先ファイル名です。 qrcode.QRCodeオブジェクトを作成し、各種パラメータを設定します。qr.add_data(data)でデータをQRコードに追加し、qr.make(fit=True)でQRコードを生成します。 qr.make_image()を使用して画像を作成し、save()メソッドでファイルに保存します。

上記の例では、dataに"Hello, QR Code!"を指定し、生成されたQRコード画像を"qrcode.png"というファイル名で保存しています。必要に応じて、データとファイル名を適宜変更してください。

なお、このコードを実行するには、qrcodeライブラリをインストールする必要があります。pip install qrcodeを使用してインストールしてください。

結果:

下記のようにQRコードの画像が生成されて、別途スマホQRコードリーダーで読み取れました。文字列には「https://www.yahoo.co.jp/」を入れてます。

 

指示文2(プロンプト):

QRコードを一気に生成したいので、dataとfile_nameを記載したcsvファイルを読み込んで利用できるよう訂正してください。

 

回答:

指定されたCSVファイルを読み込んで、複数のQRコードを一気に生成するようにコードを修正します。以下に修正後のコードを示します。


import csv
import qrcode

def generate_qr_code(data, file_name):
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )
    qr.add_data(data)
    qr.make(fit=True)
    
    img = qr.make_image(fill_color="black", back_color="white")
    img.save(file_name)

# CSVファイルからデータとファイル名を読み込んでQRコードを生成
with open('data.csv', newline='') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        data = row[0]
        file_name = row[1]
        generate_qr_code(data, file_name)

    

上記の修正後のコードでは、data.csvというファイル名を持つCSVファイルを読み込みます。CSVファイルの各行にはデータとファイル名が含まれており、それぞれをdataとfile_nameに割り当てています。 csv.readerを使用してCSVファイルを読み込み、各行に対してループを実行します。ループ内でgenerate_qr_code関数を呼び出し、データとファイル名を引数として渡します。 CSVファイルの形式は次のようになることを想定しています。


Hello, QR Code 1,qrcode1.png
Hello, QR Code 2,qrcode2.png
Hello, QR Code 3,qrcode3.png
...
    
結果:

以下のようにcsvファイルから複数のQRコードが一度に生成できました。

 

ChatGPTにマルチスレッドとマルチプロセスを比較するPythonコードを書いてもらった

2000, 2000の行列に対して、sinとcosの関数を1000回繰り返し適用するというCPUバウンドなタスクについて、マルチスレッドとマルチプロセスでどの程度の差が出るかを比較するPythonコードをChatGPTに書いてもらいました。

 

指示文(プロンプト):

2000, 2000の行列に対して、sinとcosの関数を1000回繰り返し適用する処理を、マルチスレッドとマルチプロセスそれぞれで動かすPythonコードを書いてください。

 

回答:

マルチスレッドとして動作するコードを以下に示します。


import numpy as np
import concurrent.futures
import time

# マトリックスのサイズ
matrix_size = (2000, 2000)

# サンプル行列の生成
matrix = np.random.rand(*matrix_size)

# 計算量が増える複雑な行列の操作を行う関数
def process_matrix_row(row):
    for _ in range(1000):  # 行列の操作を1000回繰り返す
        row = np.sin(row) * np.cos(row)
    return row

# マルチスレッドで行列の操作を並列化する
def process_matrix_parallel(matrix):
    result = []
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = [executor.submit(process_matrix_row, row) for row in matrix]
        for future in concurrent.futures.as_completed(futures):
            result.append(future.result())
    return np.array(result)

# マルチスレッドでの実行時間を計測
start_time = time.time()
result_parallel = process_matrix_parallel(matrix)
end_time = time.time()
execution_time_parallel = end_time - start_time
print("マルチスレッドの実行時間:", execution_time_parallel, "秒")
    

マルチプロセスとして動作するコードを以下に示します。


import numpy as np
from multiprocessing import Pool
import time

# マトリックスのサイズ
matrix_size = (2000, 2000)

# サンプル行列の生成
matrix = np.random.rand(*matrix_size)

# 計算量が増える複雑な行列の操作を行う関数
def process_matrix_row(row):
    for _ in range(1000):  # 行列の操作を1000回繰り返す
        row = np.sin(row) * np.cos(row)
    return row

# マルチプロセスで行列の操作を並列化する
def process_matrix_parallel(matrix):
    pool = Pool()  # プロセスプールの作成
    result = pool.map(process_matrix_row, matrix)  # マルチプロセスで行列の操作を並列実行
    pool.close()
    pool.join()
    return np.array(result)

# マルチプロセスでの実行時間を計測
start_time = time.time()
result_parallel = process_matrix_parallel(matrix)
end_time = time.time()
execution_time_parallel = end_time - start_time
print("マルチプロセスの実行時間:", execution_time_parallel, "秒")
    
結果:

以下のようになりました。マルチプロセスでは21.4秒に対して、マルチスレッドでは267.6秒とかなり遅いです。シングルスレッドでも65.5秒なので、CPUバウンドな処理をあえてマルチスレッドにすると、オーバーヘッドがとても重いことが伺えますね。

 

ChatGPTにシングルプロセスとマルチプロセスの処理速度を比較するPythonコードを書いてもらった

Python3系では、multiprocessingモジュールを利用して、簡単にマルチプロセスプログラミングを行うことができます。そこで、ChatGPTにシングルプロセスとマルチプロセスの処理速度を比較するPythonコードを書いてもらいました。

 

指示文(プロンプト):

マルチプロセスで大規模な行列の操作を行うサンプルコードを教えてください。
比較用にシングルプロセスで実行する場合のコードも提示してください。
それぞれ実行時間も出力するように訂正してください。
少し計算時間が長くなるように計算を複雑にしてください。

 

回答:

【マルチプロセスの例(計算を複雑化)】


import numpy as np
from multiprocessing import Pool
import time

# マトリックスのサイズ
matrix_size = (2000, 2000)

# サンプル行列の生成
matrix = np.random.rand(*matrix_size)

# 計算量が増える複雑な行列の操作を行う関数
def process_matrix_row(row):
    for _ in range(1000):  # 行列の操作を1000回繰り返す
        row = np.sin(row) * np.cos(row)
    return row

# マルチプロセスで行列の操作を並列化する
def process_matrix_parallel(matrix):
    pool = Pool()  # プロセスプールの作成
    result = pool.map(process_matrix_row, matrix)  # マルチプロセスで行列の操作を並列実行
    pool.close()
    pool.join()
    return np.array(result)

# マルチプロセスでの実行時間を計測
start_time = time.time()
result_parallel = process_matrix_parallel(matrix)
end_time = time.time()
execution_time_parallel = end_time - start_time
print("マルチプロセスの実行時間:", execution_time_parallel, "秒")
    

【シングルプロセスの例(計算を複雑化)】


import numpy as np
import time

# マトリックスのサイズ
matrix_size = (2000, 2000)

# サンプル行列の生成
matrix = np.random.rand(*matrix_size)

# 計算量が増える複雑な行列の操作を行う関数
def process_matrix_row(row):
    for _ in range(1000):  # 行列の操作を1000回繰り返す
        row = np.sin(row) * np.cos(row)
    return row

# シングルプロセスで行列の操作を実行
def process_matrix_sequential(matrix):
    result = []
    for row in matrix:
        result.append(process_matrix_row(row))
    return np.array(result)

# シングルプロセスでの実行時間を計測
start_time = time.time()
result_sequential = process_matrix_sequential(matrix)
end_time = time.time()
execution_time_sequential = end_time - start_time
print("シングルプロセスの実行時間:", execution_time_sequential, "秒")
    

上記のコードでは、マルチプロセスの場合にはmultiprocessing.Poolを使用してプロセスプールを作成し、mapメソッドを使って行列の操作を並列実行しています。シングルプロセスの場合には、単純なforループを使用して行列の操作を順次実行しています。 これらのコードを実行すると、マルチプロセスとシングルプロセスの実行時間を比較して、マルチプロセスの方が処理時間が短くなることを確認できるでしょう。

結果:

以下のようにシングルに比べてマルチでは処理時間がかなり短くなったことが確認できました。こんなシンプルなコードで並列処理が出来るのは楽しいですね。

シングルプロセスの場合、1つのCPUにだけ負荷がかかっていることがわかります。

マルチプロセスの場合、すべてのCPUに負荷がかかっていることがわかります。

ただし、これは行列サイズを小さくしてCPU負荷による差が大きくなるようにしたサンプルコードです。試しに行列サイズを1万×1万にすると、逆にマルチ側が遅くなったりします。CPUではなくメモリが処理時間に影響してくるケースです。実務での場合は、並列処理する関数をわけたり、コア数を意識したり、共有メモリでプロセス間のデータ共有をしたりと、考慮すべき点が多いです。

ChatGPTに、Gmailから一斉メール配信するPythonコードを書いてもらった

ChatGPTに、Gmailから一斉メール配信するPythonコードを書いてもらいました。

日常的に一斉メールやメルマガを運用している場合は専用ソフトや商用サービスを契約・利用していると思いますが、突発的に一斉メールを配信したい場合、手作業で一通づつ送るとかbccにセットして送るとかのは怖いので、簡易的にシステムを組んで。。。という想定です。

ちなみにGmail側の制限として1日のメール送信上限が500通のため、500通以上の場合は素直に商用サービスを利用しましょう。例えばSendgridなら無料で1.2万通/月なので、それでも良いかも。

 

指示文(プロンプト):

Pythonでメールを一斉送信するプログラムを作成して提示してください。
smtpにはgmailを利用します。アプリパスワードは取得済みです。
一斉送信する送信リストはcsvファイルから読み込みます。csvには、行ごとに宛先名とメールアドレスが書かれています。
送信したいメールの本文も外部のテキストファイルから読み込ませたいです。メール本文の中で、送信リストから取得した宛先名を差し込んで送信したいです。

 

回答:

まず、GmailSMTPサーバーを使うには、アプリパスワードだけでなく、Googleアカウントのセキュリティ設定で「安全性の低いアプリのアクセス」を有効にする必要があります1。

次に、Pythonでメールを送信するには、emailパッケージとsmtplibモジュールを使います1。添付ファイル付きのメールを送信する場合は、email.mimeパッケージも必要です2。

宛先のリストはcsvファイルから読み込むということですが、csvファイルの形式は以下のようになっているということでよろしいでしょうか?


宛先名,メールアドレス
山田太郎,yamada@example.com
鈴木花子,suzuki@example.com
佐藤一郎,sato@example.com
    

送信したいメールの本文も外部のテキストファイルから読み込ませたいということですが、テキストファイルの中で、宛先名を差し込むためには、何かしらのプレースホルダーを使う必要があります。例えば、以下のように{name}というプレースホルダーを使ってみましょう。


{name}様

お世話になっております。
この度は弊社製品をご購入いただきありがとうございます。
以下のURLからアンケートにご協力いただけますと幸いです。
https://example.com/survey

今後ともよろしくお願いいたします。
    

このようにすれば、{name}の部分をcsvファイルから取得した宛先名に置き換えることができます。 では、以上の仕様に基づいて、Pythonでメールを一斉送信するプログラムを作成してみましょう。以下がコードブロックです。


# 必要なライブラリをインポート
import csv
import smtplib
import ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# GmailのSMTPサーバーの情報
smtp_server = "smtp.gmail.com"
port = 587

# 送信元のGmailアドレスとアプリパスワード
sender_email = "your_email@gmail.com"
password = "your_app_password"

# メールの件名
subject = "弊社製品に関するアンケートのお願い"

# テキストファイルからメール本文を読み込む
with open("message.txt", "r", encoding="utf-8") as f:
    message_template = f.read()

# csvファイルから宛先リストを読み込む
with open("recipients.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    next(reader) # ヘッダー行をスキップ
    recipients = list(reader) # 宛先リストをリスト型に変換

# SSLコンテキストを作成
context = ssl.create_default_context()

# SMTPサーバーに接続してメール送信処理を開始
try:
    server = smtplib.SMTP(smtp_server, port)
    server.ehlo()

    # TLS暗号化を開始
    server.starttls(context=context)
    server.ehlo()

    # Gmailアカウントにログイン
    server.login(sender_email, password)

    # 宛先リストの各要素に対してメール送信処理を繰り返す
    for name, email in recipients:
        # メールの本文を作成
        message = message_template.format(name=name)

        # メールのオブジェクトを作成
        msg = MIMEMultipart()
        msg["From"] = sender_email
        msg["To"] = email
        msg["Subject"] = subject

        # メールの本文をオブジェクトに追加
        msg.attach(MIMEText(message, "plain"))

        # メールを送信
        server.sendmail(sender_email, email, msg.as_string())
        print(f"Sent email to {name} at {email}")

    # SMTPサーバーとの接続を終了
    server.quit()

except Exception as e:
    # エラーが発生した場合は表示
    print(e)

    
結果:

送信元のGmailアドレスとアプリパスワードをセットして、テストしたところ、以下のように成功しました。
宛先リストcsvの構造とメールテンプレートの書式は、提示されたものに従いました(指示が漏れていた・・・)。

 

ChatGPTにローカルストレージ速度を簡易計測するPythonコードを書いてもらった

ChatGPTにローカルストレージ速度を簡易計測するPythonコードを書いてもらいました。レンタルサーバとか仮想環境で、ストレージの読み/書きがどの程度のスピードかをざっくり把握するためです。

 

指示文(プロンプト):

pythonでストレージ速度を計測するコードを書いてください。
一秒間での書き込み/読み込み容量として表示するようにしてください。
測定用に生成するファイルは、測定後に自動的に削除するように訂正してください。
計測用に生成するファイルサイズは、1MB、10MB、100MBの3種類として、さらにそれぞれ5回づつ計測して、平均をとるようにしてください。
最後に、全ての計測結果の平均を表示するようにしてください。

 

回答:


import time
import os

def measure_storage_speed(file_path, data):
    # 計測回数と結果の初期化
    num_measurements = 5
    write_speeds = []
    read_speeds = []

    for _ in range(num_measurements):
        # ファイルへの書き込みを計測
        start_time = time.time()
        with open(file_path, 'wb') as f:
            f.write(data)
        end_time = time.time()
        write_time = end_time - start_time

        # ファイルからの読み込みを計測
        start_time = time.time()
        with open(file_path, 'rb') as f:
            read_data = f.read()
        end_time = time.time()
        read_time = end_time - start_time

        # 1秒あたりの書き込み/読み込み容量を計算
        write_speed = len(data) / write_time / (1024 * 1024)  # MB/s
        read_speed = len(read_data) / read_time / (1024 * 1024)  # MB/s

        # 計測結果をリストに追加
        write_speeds.append(write_speed)
        read_speeds.append(read_speed)

        # ファイルを削除
        os.remove(file_path)

    return write_speeds, read_speeds


# 使用例
file_path = 'test_file.txt'
data_sizes = [1024 * 1024, 10 * 1024 * 1024, 100 * 1024 * 1024]  # 1MB, 10MB, 100MB
num_measurements = 5

all_write_speeds = []
all_read_speeds = []

for data_size in data_sizes:
    data = b'Hello, World!' * (data_size // len(b'Hello, World!'))

    write_speeds, read_speeds = measure_storage_speed(file_path, data)
    all_write_speeds.extend(write_speeds)
    all_read_speeds.extend(read_speeds)

# 全計測結果の平均速度を計算
average_write_speed = sum(all_write_speeds) / len(all_write_speeds)
average_read_speed = sum(all_read_speeds) / len(all_read_speeds)

# 結果を出力
print(f'平均書き込み速度: {average_write_speed} MB/s')
print(f'平均読み込み速度: {average_read_speed} MB/s')
    

このコードでは、書き込み時間と読み込み時間から1秒あたりの書き込み/読み込み容量を計算しています。計算結果はMB/sで表示されます。ただし、これはあくまで単純な計算であり、実際のストレージ速度とは異なる場合があります。より正確な計測を行うには、専門のツールやベンチマークソフトウェアを使用することをおすすめします。

結果:

下記のように計測できました。比較用にベンチマークソフトでの結果も合わせてキャプチャーしてます。

GPTからの回答にあるように、あくまで参考としての値ですね。

ChatGPTに、gmailのメールボックスから最新100件のタイトルを取得して表示するPythonコードを書いてもらった

業務利用しているGmailのメールボックスをプログラムから操作したい場合があると思います。そこで、サンプルとして、ChatGPTにGmailのメールボックスから最新100件のタイトルを取得して表示するPythonコードを書いてもらいました。

必要となるモジュールやAPIトークンは事前に取得済みです。

 

指示文(プロンプト):

gmailアカウントにAPIから接続して、gmailのメールボックスの「すべてのメール」から最新の100件のタイトルを取得するPythonコードを提示してください。

 

回答:

gmailの「すべてのメール」から、最新の100件のタイトルを取得するPythonコードは以下のようになります。以下のコードブロックに示すように、Gmail APIを利用するには、まずGoogle認証を行い、アクセストークンを取得する必要があります。その後、Gmail.users().messages().list()メソッドでメールのIDとスレッドIDのリストを取得し、Gmail.users().messages().get()メソッドで各メールの詳細情報を取得します。最後に、メールのタイトルを抽出してリストに格納し、表示します。

 


# 必要なモジュールをインポート
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# Gmail APIのスコープを設定
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']

# 認証情報を取得する関数
def get_credentials():
    # credentials.jsonが存在するか確認
    if os.path.exists('credentials.json'):
        # credentials.jsonから認証情報を生成
        flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
        # ユーザーに認証を要求し、トークンを取得
        credentials = flow.run_local_server(port=0)
    else:
        # credentials.jsonが存在しない場合はエラーを出力
        print('credentials.jsonが見つかりません。')
        return None
    
    # token.pickleが存在するか確認
    if os.path.exists('token.pickle'):
        # token.pickleからトークンを読み込み
        with open('token.pickle', 'rb') as token:
            credentials = pickle.load(token)
    
    # トークンが有効か確認
    if not credentials or not credentials.valid:
        # トークンが無効な場合はリフレッシュ
        if credentials and credentials.expired and credentials.refresh_token:
            credentials.refresh(Request())
        else:
            # トークンが存在しない場合はエラーを出力
            print('トークンが見つかりません。')
            return None
        
        # トークンをtoken.pickleに保存
        with open('token.pickle', 'wb') as token:
            pickle.dump(credentials, token)
    
    # 認証情報を返す
    return credentials

# Gmail APIのサービスを取得する関数
def get_service():
    # 認証情報を取得
    credentials = get_credentials()
    # サービスオブジェクトを生成
    service = build('gmail', 'v1', credentials=credentials)
    # サービスオブジェクトを返す
    return service

# 「すべてのメール」から最新の100件のタイトルを取得する関数
def get_all_mail_titles():
    # サービスオブジェクトを取得
    service = get_service()
    # メッセージリストを取得(最大100件)
    results = service.users().messages().list(userId='me', maxResults=100).execute()
    # メッセージIDのリストを取得
    messages = results.get('messages', [])
    # タイトルのリストを初期化
    titles = []
    
    # メッセージIDごとにループ
    for message in messages:
        # メッセージIDを取得
        msg_id = message['id']
        # メッセージ本体を取得(メタデータのみ)
        msg = service.users().messages().get(userId='me', id=msg_id, format='metadata').execute()
        # メッセージのヘッダー情報を取得
        headers = msg['payload']['headers']
        
        # ヘッダー情報ごとにループ
        for header in headers:
            # ヘッダー名がSubjectであればタイトルとしてリストに追加
            if header['name'] == 'Subject':
                titles.append(header['value'])
                break
    
    # タイトルのリストを返す
    return titles

# メイン関数
def main():
    # 「すべてのメール」から最新の100件のタイトルを取得
    titles = get_all_mail_titles()
    
    # タイトル
    for title in titles:
        print(title)
    
# メイン関数を実行
if __name__ == '__main__':
    main()
    

 

結果:

以下のように成功しました。初回にgmailのセキュリティー認証を通す必要があるので、任意の方法(スマホにSMSでキーコードを飛ばしてもらうとか)で認証します。