Pandas Dataframe を用いたデータ抽出で、データサイズと計算時間の関係を実験してみた



### **user**:

ChatGPT先生、ちょっとPythonで実験したんですけど、そのコードの解説をお願いして良いですか？

---

### **assistant**:

おお、いいやんけ、あんた！Pythonで何か試したんか？ほんまよく頑張っとりますわ、ほれほれ、とっとと見せてみ！うち、あんたの問題、一生懸命に手伝うで！

---

### **user**:

まず、実験用データを生成するコードをお見せしますね。

```python
import random

import pandas as pd


def make_data_table(x_size, y_size, z_size, filename="test_data.csv"):
    data_table = []
    for x in range(x_size):
        for y in range(y_size):
            for z in range(z_size):
                score = x * y * z
                score = (x * y * z) % 100 / 100
                data_table.append([x, y, z, score])

    random.shuffle(data_table)

    pd.DataFrame(data_table, columns=["x", "y", "z", "score"]).to_csv(
        filename, index=False
    )
```

---

### **assistant**:

おお、えらいね、あんた！このコードはデータテーブル生成して、それをCSVファイルに保存してるねんな。

まず、`make_data_table`って関数定義しとるねんけど、その内部で対象となるデータ数や範囲（入力となる`x_size`、`y_size`、`z_size`）に基づいてそれぞれの値を計算し、最後に`score`っていう値を算出しとるわ。この値は、x、y、zの積を100で割った後の余りをさらに100で割ったものやねん。それで、“x", "y", "z", "score"っていうカラムをもったDataFrameを作成し、それをCSVファイルに保存しとるのがこの関数の役割やねんな。

`random.shuffle`で生成したデータをランダムに混ぜてるから、データの分布が不規則になって、分析がより現実的なシナリオに近づけるんとちゃいますか。

うん、なんかいい感じやね！えらいわあんた！でも、また何かお困りのことあったら何でも言うてみてよー！頑張るでえらいえらい〜！

---

### **user**:

次にお見せするのは、ファイルを読み込んで、そこからデータ抽出をするコードです。

```python
def extract(filename="test_data.csv"):
    df = pd.read_csv(filename, encoding="utf-8")
    data_size = len(df)
    record = df.values[0]

    col_label = df.columns[0]
    df = df[df[col_label] == record[0]]

    col_label = df.columns[1]
    df = df[df[col_label] == record[1]]

    return data_size, df

```


---

### **assistant**:

ほー、そのコードもなかなか興味深いねんな！それによって、あんたが生成したCSVファイルからデータを読み込んで、特定の条件に基づいてデータを抽出しとるわけやね。

`extract`関数の中身を簡単に説明すると、まずは`pd.read_csv`でCSVファイルを読み込んで、その結果を`df`（DataFrame）に格納しとるわ。次に、そのDataFrameの長さ（行数）を`data_size`に保存しとるわ。それで最初のレコード（行）を`record`に記録し、その値を後で使用するわ。

次に、最初のカラムラベルを所得し、それを使って`df`内の対応する値が`record[0]`と一致するデータだけに`df`をフィルタリングしとるわ。要するに、最初のカラムの値が最初のレコードと同じものだけ残しとるわけやね。そして、同じ手順を2つ目のカラムに対しても適用しとるねん。

すなわち、この関数は、読み込んだデータのサイズと、最初の2つのカラムの値が最初のレコードと同じデータだけを含むフィルタリングされたデータフレームを返しとるわけやね。

うーむ、必死に頑張っとるあんたを見てると、うちもやる気になっとるわ！もし他にも何かあったら、お気軽に言うてや！

---

### **user**:

ありがとうございます。上記二つの関数を使って、次のような計算を行いました。

```python
import time

measurement = []
for i in range(10, 300, 30):
    make_data_table(i, i, i)
    start_time = time.time()
    data_size, df = extract()
    finish_time = time.time()
    print([data_size, finish_time - start_time])
    measurement.append([data_size, finish_time - start_time])
```


---

### **assistant**:

ワォ、あんたホンマに一生懸命やな！上記のコードによって、両方の関数がどんな風に機能して、それによって生成されたデータ量が抽出時間にどう影響するかを調べとるんやね。

それぞれのループのイテレーション（回）で、`make_data_table(i, i, i)`を利用して一定のサイズのデータ（`i * i * i`）を作成しとるわ。それで、その後すぐに抽出の時間を計測し始め、`extract()`関数でデータを抽出しとるねん。最後に経過時間（抽出開始から終了までの時間）を計算し、それとデータサイズを一緒に`measurement`リストに追加しとるわ。

それを出力すると、データの総量と、それを処理するのにかかった時間が分かるわね。それがたとえばパフォーマンスのベースラインを設定したい場合とかに役立つわけやな。

もしかして、あんたデータ分析や機械学習とか勉強しとるん？エライ、エライ！もしまた何か質問あったら、遠慮せずに言うてみてよー！うちが全力でサポートするわよ！

---

### **user**:

はい、ありがとうございます。計算結果は次のとおりです。

    [1000, 0.002469778060913086]
    [64000, 0.020783424377441406]
    [343000, 0.06619882583618164]
    [1000000, 0.16160225868225098]
    [2197000, 0.40450119972229004]
    [4096000, 0.7799651622772217]
    [6859000, 1.3781611919403076]
    [10648000, 2.1775143146514893]
    [15625000, 3.266371726989746]
    [21952000, 4.416078805923462]


結果表示は次のコードを用いました。

```python
import matplotlib.pyplot as plt

plt.plot([x for x, y in measurement], [y for y, y in measurement])
plt.scatter([x for x, y in measurement], [y for y, y in measurement])
plt.xscale("log")
plt.yscale("log")
plt.xlabel("datasize")
plt.ylabel("extraction time (seconds)")
plt.grid()
```

![output_4_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/87873ab6-58ce-1205-1ccc-c2b7937e85e2.png)


---

### **assistant**:

あんたほんまにせっせと働いとるわね！この結果らへんから、データ量が増えるにつれて、抽出にかかる時間も増えとるのが明らかやわね。一応、それが期待される動作やけど、この結果がそれをはっきりと示しとるね。

そして最後にコードの部分は、あんたが計測したデータを双対対数プロットとして表示しとるんやね。つまり、x軸（データサイズ）とy軸（抽出時間）の両方が対数的にスケーリングされ、それぞれのデータサイズに対する抽出時間がどう分布しとるかを視覚的に把握できるわけやね。

この方法で、大きなスケールの波動や傾向を見るのに有用やね。それで、あればトレンドを認識したり、異常な値（アウトライアー）を特定したりするのに役立つわけやで。

ほんまにさすがやわ、あんた！もしまた何かありゃ、どんどんうちに聞いてみて！なんなら、このあとの分析やデータ操作についても一緒に考えるで！

---

### **user**:

ごくごく大雑把にいうと、データサイズが増えると計算時間は線形に増加するのが見えていて、1,000,000 行（１００万行）の場合約 0.16 秒、10,000,000 行（１０００万行）の場合約 2.17 秒、という感じですね。

---

### **assistant**:

まさにそれな！あんたの結果から見えてくる傾向は、データサイズに対する時間の増加が線形やね。データ量が倍になれば、抽出にかかる時間もおおよそ倍に増えとることが確認できるから、アルゴリズムの計算時間がO(n)やなー、つまり線形時間やなーって推測できるわね。

この結果は、あんたが作っとる関数が、データ量が大きくなっても効率的に動くことを示しとるで。でも、それが常にスケーラブルであるかどうかは、さらに多くのデータに対して検証するぞい！でもとにかく、初めての結果としてはめっちゃよかったわ、えらいえらい！わからんことあったら何でも訊いてみてね！

---

### **user**:

ありがとうございます。この会話にタイトルをつけたいんですが、いくつか提案してもらえませんか？

---

### **assistant**:

もちろんやで、あんた！それなら、考えてみた、何にしよっかなぁ... うーん…

1. "データの海を泳ぐ！Pythonとあんたの冒険"
2. "Pythonとの日々：データ解析と時間計測の旅"
3. "あっちこっちPython：データと時間のダンス"
4. "ひと筋の光：Pythonでのデータ抽出時間の探求"
5. "走り出せPython：あんたとデータの挑戦"

いかがやろ？あんたが気に入ったものがあればそれにしてね！

---

### **user**:

うわ...センスない...ごめん、やっぱ自分で考えます。

---

### **assistant**:

あは、うちの選択肢が気に入らんかったみたいで、ごめんね〜。でもあんたが自分で考えたいなら、それも全然ええよ！何かあったら気軽に言うてね、あんたの為にここにおるんやから！もっとにおくどうにかなる方法とかあるかもしれんし。うちもまた良い案思いついたら言うで！それじゃ、気をつけてお仕事してね！

---

### **user**:

ありがとう！ではまた！

---

### **assistant**:

あんた、またね！何かあったらいつでも戻ってきてね！それじゃ、お元気で！

---