様々なデータ分析問題

PythonPythonBeginner
今すぐ練習

This tutorial is from open-source community. Access the source code

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

この実験では、様々なPythonのデータコンテナを扱い、リスト内包表記、セット内包表記、辞書内包表記を利用する方法を学びます。また、データ処理に役立つツールを提供するcollectionsモジュールを探索します。

Pythonはデータ操作と分析に強力なツールを提供しています。この実験では、Pythonの組み込みデータ構造と特殊なツールを使って、さまざまなデータセットを分析する練習を行います。簡単なポートフォリオデータセットから始めて、シカゴ交通局のバスデータを分析して有意義な洞察を引き出すまで進めます。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ControlFlowGroup(["Control Flow"]) python(("Python")) -.-> python/DataStructuresGroup(["Data Structures"]) python(("Python")) -.-> python/FileHandlingGroup(["File Handling"]) python(("Python")) -.-> python/PythonStandardLibraryGroup(["Python Standard Library"]) python(("Python")) -.-> python/DataScienceandMachineLearningGroup(["Data Science and Machine Learning"]) python/ControlFlowGroup -.-> python/list_comprehensions("List Comprehensions") python/DataStructuresGroup -.-> python/lists("Lists") python/DataStructuresGroup -.-> python/dictionaries("Dictionaries") python/DataStructuresGroup -.-> python/sets("Sets") python/FileHandlingGroup -.-> python/file_reading_writing("Reading and Writing Files") python/FileHandlingGroup -.-> python/file_operations("File Operations") python/PythonStandardLibraryGroup -.-> python/data_collections("Data Collections") python/DataScienceandMachineLearningGroup -.-> python/data_analysis("Data Analysis") subgraph Lab Skills python/list_comprehensions -.-> lab-132438{{"様々なデータ分析問題"}} python/lists -.-> lab-132438{{"様々なデータ分析問題"}} python/dictionaries -.-> lab-132438{{"様々なデータ分析問題"}} python/sets -.-> lab-132438{{"様々なデータ分析問題"}} python/file_reading_writing -.-> lab-132438{{"様々なデータ分析問題"}} python/file_operations -.-> lab-132438{{"様々なデータ分析問題"}} python/data_collections -.-> lab-132438{{"様々なデータ分析問題"}} python/data_analysis -.-> lab-132438{{"様々なデータ分析問題"}} end

辞書とCSVデータの扱い

まずは、株式保有に関する簡単なデータセットを調べてみましょう。このステップでは、CSVファイルからデータを読み取り、辞書を使って構造化された形式で保存する方法を学びます。

CSV(Comma-Separated Values、カンマ区切り値)ファイルは、表形式のデータを保存する一般的な方法で、各行が1つのレコードを表し、値はカンマで区切られています。Pythonの辞書は、キーと値のペアを格納できる強力なデータ構造です。辞書を使うことで、CSVファイルからのデータをより意味のある形で整理することができます。

まず、WebIDEで新しいPythonファイルを作成します。手順は以下の通りです。

  1. WebIDEの「New File」ボタンをクリックします。
  2. ファイル名をreadport.pyとします。
  3. 以下のコードをファイルにコピーして貼り付けます。
## readport.py

import csv

## A function that reads a file into a list of dictionaries
def read_portfolio(filename):
    portfolio = []
    with open(filename) as f:
        rows = csv.reader(f)
        headers = next(rows)   ## Skip the header row
        for row in rows:
            record = {
                'name': row[0],
                'shares': int(row[1]),
                'price': float(row[2])
            }
            portfolio.append(record)
    return portfolio

このコードは、read_portfolioという関数を定義しており、いくつかの重要なタスクを実行します。

  1. filenameパラメータで指定されたCSVファイルを開きます。open関数を使ってファイルにアクセスし、with文を使ってファイルの読み取りが終わった後に適切に閉じることを保証します。
  2. ヘッダ行をスキップします。ヘッダ行には通常、CSVファイルの列名が含まれています。next(rows)を使ってイテレータを次の行に移動させ、ヘッダ行をスキップします。
  3. 各データ行に対して、辞書を作成します。辞書のキーは'name'、'shares'、'price'です。これらのキーを使うことで、データにより直感的にアクセスすることができます。
  4. 株式数を整数に、価格を浮動小数点数に変換します。CSVファイルから読み取ったデータは最初は文字列形式であるため、計算には数値が必要です。
  5. 各辞書をportfolioというリストに追加します。このリストには、CSVファイルのすべてのレコードが含まれます。
  6. 最後に、辞書の完全なリストを返します。

次に、交通データ用のファイルを作成しましょう。readrides.pyという名前の新しいファイルを作成し、以下の内容を記述します。

## readrides.py

import csv

def read_rides_as_dicts(filename):
    """
    Read the CTA bus data as a list of dictionaries
    """
    records = []
    with open(filename) as f:
        rows = csv.reader(f)
        headers = next(rows)   ## Skip header
        for row in rows:
            route = row[0]
            date = row[1]
            daytype = row[2]
            rides = int(row[3])
            record = {
                'route': route,
                'date': date,
                'daytype': daytype,
                'rides': rides
            }
            records.append(record)
    return records

このread_rides_as_dicts関数は、read_portfolio関数と同様の動作をします。CTAバスデータに関連するCSVファイルを読み取り、ヘッダ行をスキップし、各データ行に対して辞書を作成し、これらの辞書をリストに格納します。

では、WebIDEでターミナルを開き、read_portfolio関数をテストしましょう。

  1. 「Terminal」メニューをクリックし、「New Terminal」を選択します。
  2. python3と入力してPythonインタープリタを起動します。
  3. 以下のコマンドを実行します。
>>> from readport import read_portfolio
>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')
>>> from pprint import pprint
>>> pprint(portfolio)
[{'name': 'AA', 'price': 32.2, 'shares': 100},
 {'name': 'IBM', 'price': 91.1, 'shares': 50},
 {'name': 'CAT', 'price': 83.44, 'shares': 150},
 {'name': 'MSFT', 'price': 51.23, 'shares': 200},
 {'name': 'GE', 'price': 40.37, 'shares': 95},
 {'name': 'MSFT', 'price': 65.1, 'shares': 50},
 {'name': 'IBM', 'price': 70.44, 'shares': 100}]

ここでは、pprint関数(整形出力)を使ってデータをより読みやすい形式で表示しています。リスト内の各アイテムは、1つの株式保有を表す辞書です。辞書には以下のキーがあります。

  • 株式シンボル (name):株式を識別するために使用される略称です。
  • 保有株式数 (shares):保有している株式の数を示します。
  • 1株あたりの購入価格 (price):各株を購入した価格です。

'MSFT'や'IBM'などの一部の株式が複数回出現していることに注意してください。これらは、同じ株式の異なる購入を表しており、異なる時期に異なる価格で購入された可能性があります。

✨ 解答を確認して練習

リスト内包表記、セット内包表記、辞書内包表記の使用

Pythonの内包表記は、既存のコレクションに基づいて新しいコレクションを作成する非常に便利で簡潔な方法です。Pythonのコレクションには、リスト、セット、辞書などがあり、これらはさまざまな種類のデータを格納するコンテナのようなものです。内包表記を使うと、特定のデータをフィルタリングしたり、データを何らかの方法で変換したり、より効率的に整理したりすることができます。このセクションでは、ポートフォリオデータを使って、これらの内包表記がどのように機能するかを探ってみましょう。

まず、前のステップと同じようにPythonターミナルを開く必要があります。ターミナルが開いたら、以下の例を1つずつ入力します。この実践的なアプローチにより、内包表記が実際にどのように機能するかを理解するのに役立ちます。

リスト内包表記

リスト内包表記は、Pythonにおける特殊な構文で、新しいリストを作成します。既存のコレクション内の各アイテムに式を適用することで、これを行います。

例から始めましょう。まず、ポートフォリオデータを読み取る関数をインポートします。次に、リスト内包表記を使って、ポートフォリオから特定の保有株をフィルタリングします。

>>> from readport import read_portfolio
>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')

## Find all holdings with more than 100 shares
>>> large_holdings = [s for s in portfolio if s['shares'] > 100]
>>> print(large_holdings)
[{'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23}]

このコードでは、まずread_portfolio関数をインポートし、CSVファイルからポートフォリオデータを読み取ります。次に、リスト内包表記[s for s in portfolio if s['shares'] > 100]が、portfolioコレクション内の各アイテムsを調べます。保有株数が100を超える場合のみ、そのアイテムsを新しいリストlarge_holdingsに含めます。

リスト内包表記は、計算を行うためにも使用できます。いくつかの例を見てみましょう。

## Calculate the total cost of each holding (shares * price)
>>> holding_costs = [s['shares'] * s['price'] for s in portfolio]
>>> print(holding_costs)
[3220.0, 4555.0, 12516.0, 10246.0, 3835.15, 3255.0, 7044.0]

## Calculate the total cost of the entire portfolio
>>> total_portfolio_cost = sum([s['shares'] * s['price'] for s in portfolio])
>>> print(total_portfolio_cost)
44671.15

最初の例では、リスト内包表記[s['shares'] * s['price'] for s in portfolio]が、portfolio内の各アイテムについて、株式数と価格を掛け合わせて、各保有株の総コストを計算します。2番目の例では、sum関数とリスト内包表記を組み合わせて、ポートフォリオ全体の総コストを計算します。

セット内包表記

セット内包表記は、既存のコレクションからセットを作成するために使用されます。セットは、一意の値のみを含むコレクションです。

ポートフォリオデータでどのように機能するか見てみましょう。

## Find all unique stock names
>>> unique_stocks = {s['name'] for s in portfolio}
>>> print(unique_stocks)
{'MSFT', 'IBM', 'AA', 'GE', 'CAT'}

このコードでは、セット内包表記{s['name'] for s in portfolio}が、portfolio内の各アイテムsを調べ、株式名 (s['name']) をセットunique_stocksに追加します。セットは一意の値のみを格納するため、重複のないポートフォリオ内のすべての異なる株式のリストが得られます。

辞書内包表記

辞書内包表記は、式を適用してキーと値のペアを作成することで、新しい辞書を作成します。

ポートフォリオ内の各株式の総株式数をカウントするために辞書内包表記を使用する例を見てみましょう。

## Create a dictionary to count total shares for each stock
>>> totals = {s['name']: 0 for s in portfolio}
>>> for s in portfolio:
...     totals[s['name']] += s['shares']
...
>>> print(totals)
{'AA': 100, 'IBM': 150, 'CAT': 150, 'MSFT': 250, 'GE': 95}

最初の行で、辞書内包表記{s['name']: 0 for s in portfolio}が、各株式名 (s['name']) をキーとし、各キーの初期値を0とする辞書を作成します。次に、forループを使ってportfolio内の各アイテムを調べます。各アイテムについて、株式数 (s['shares']) をtotals辞書内の対応する値に加えます。

これらの内包表記は非常に強力で、数行のコードでデータを変換し、分析することができます。Pythonプログラミングのツールボックスには欠かせない便利なツールです。

collections モジュールの探索

Pythonでは、リスト、辞書、セットなどの組み込みコンテナは非常に便利です。しかし、Pythonのcollectionsモジュールは、これらの組み込みコンテナの機能を拡張した特殊なコンテナデータ型を提供することで、さらに一歩進んでいます。これらの便利なデータ型のいくつかを詳しく見てみましょう。

Pythonターミナルで作業を続け、以下の例に沿って進めます。

Counter

Counterクラスは辞書のサブクラスです。主な目的は、ハッシュ可能なオブジェクトをカウントすることです。アイテムをカウントする便利な方法を提供し、さまざまな操作をサポートしています。

まず、Counterクラスとポートフォリオを読み取る関数をインポートする必要があります。次に、CSVファイルからポートフォリオを読み取ります。

>>> from collections import Counter
>>> from readport import read_portfolio
>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')

ここで、各株式の名称ごとに株式数をカウントするCounterオブジェクトを作成します。

## Create a counter to count shares by stock name
>>> totals = Counter()
>>> for s in portfolio:
...     totals[s['name']] += s['shares']
...
>>> print(totals)
Counter({'MSFT': 250, 'IBM': 150, 'CAT': 150, 'AA': 100, 'GE': 95})

Counterオブジェクトの素晴らしい特徴の1つは、新しいキーを自動的にカウント0で初期化することです。これは、カウントを増やす前にキーが存在するかどうかをチェックする必要がなくなるため、カウントを累積するコードを簡素化します。

カウンターには特殊なメソッドもあります。たとえば、most_common()メソッドはデータ分析に非常に便利です。

## Get the two stocks with the most shares
>>> most_common_stocks = totals.most_common(2)
>>> print(most_common_stocks)
[('MSFT', 250), ('IBM', 150)]

さらに、カウンターは算術演算を使って結合することができます。

## Create another counter
>>> more = Counter()
>>> more['IBM'] = 75
>>> more['AA'] = 200
>>> more['ACME'] = 30
>>> print(more)
Counter({'AA': 200, 'IBM': 75, 'ACME': 30})

## Add two counters together
>>> combined = totals + more
>>> print(combined)
Counter({'AA': 300, 'MSFT': 250, 'IBM': 225, 'CAT': 150, 'GE': 95, 'ACME': 30})

defaultdict

defaultdictは通常の辞書に似ていますが、独自の特徴があります。まだ存在しないキーに対してデフォルト値を提供します。これにより、キーを使用する前に存在するかどうかをチェックする必要がなくなるため、コードを簡素化できます。

>>> from collections import defaultdict

## Group portfolio entries by stock name
>>> byname = defaultdict(list)
>>> for s in portfolio:
...     byname[s['name']].append(s)
...
>>> print(byname['IBM'])
[{'name': 'IBM', 'shares': 50, 'price': 91.1}, {'name': 'IBM', 'shares': 100, 'price': 70.44}]
>>> print(byname['AA'])
[{'name': 'AA', 'shares': 100, 'price': 32.2}]

defaultdict(list)を作成すると、新しいキーごとに自動的に新しい空のリストが作成されます。したがって、キーが以前に存在しなかった場合でも、直接キーの値に追加することができます。これにより、キーが存在するかどうかをチェックし、手動で空のリストを作成する必要がなくなります。

他のデフォルトファクトリ関数も使用できます。たとえば、intfloat、または独自のカスタム関数を使用することができます。

## Use defaultdict with int to count items
>>> word_counts = defaultdict(int)
>>> words = ['apple', 'orange', 'banana', 'apple', 'orange', 'apple']
>>> for word in words:
...     word_counts[word] += 1
...
>>> print(word_counts)
defaultdict(<class 'int'>, {'apple': 3, 'orange': 2, 'banana': 1})

collectionsモジュールのこれらの特殊なコンテナ型は、データを扱う際にコードをより簡潔で効率的にすることができます。

シカゴ交通局のデータを用いたデータ分析チャレンジ

これまでに、さまざまなPythonのデータ構造とcollectionsモジュールを使った練習を行ってきました。それでは、これらのスキルを実世界のデータ分析タスクに活かしてみましょう。この実験では、シカゴ交通局(CTA)のバス乗車率データを分析します。この実践的なアプリケーションを通じて、Pythonを使って実世界のデータセットから有意義な情報を抽出する方法を理解することができます。

データの理解

まず、扱う交通データを見てみましょう。Pythonターミナルで、データをロードして基本構造を理解するためのコードを実行します。

>>> import readrides
>>> rows = readrides.read_rides_as_dicts('/home/labex/project/ctabus.csv')
>>> print(len(rows))
## This will show the number of records in the dataset

>>> ## Let's look at the first record to understand the structure
>>> import pprint
>>> pprint.pprint(rows[0])

import readrides文は、CSVファイルからデータを読み取る関数を持つカスタムモジュールをインポートします。readrides.read_rides_as_dicts関数は、指定されたCSVファイルからデータを読み取り、各行を辞書に変換します。len(rows)はデータセット内のレコードの総数を返します。pprint.pprint(rows[0])を使って最初のレコードを印刷することで、各レコードの構造を明確に確認することができます。

このデータには、さまざまなバス路線の日々の乗車記録が含まれています。各レコードには以下の情報が含まれています。

  • route:バス路線番号
  • date:"YYYY - MM - DD"形式の日付
  • daytype:平日は"W"、土曜日は"A"、日曜日/祝日は"U"
  • rides:その日の乗車人数

分析タスク

チャレンジの各質問を1つずつ解いていきましょう。

質問1:シカゴには何本のバス路線がありますか?

この質問に答えるには、データセット内のすべての一意の路線番号を見つける必要があります。このタスクにはセット内包表記を使用します。

>>> ## Get all unique route numbers using a set comprehension
>>> unique_routes = {row['route'] for row in rows}
>>> print(len(unique_routes))

セット内包表記は、セットを作成する簡潔な方法です。この場合、rowsリストの各行を反復処理し、route値を抽出します。セットは一意の要素のみを格納するため、すべての一意の路線番号のセットが得られます。このセットの長さを印刷することで、一意のバス路線の総数がわかります。

これらの路線の一部を見ることもできます。

>>> ## Print a few of the route numbers
>>> print(list(unique_routes)[:10])

ここでは、一意の路線のセットをリストに変換し、そのリストの最初の10要素を印刷します。

質問2:2011年2月2日に22号バスには何人が乗車しましたか?

この質問に答えるには、与えられた路線と日付に一致する特定のレコードを見つけるためにデータをフィルタリングする必要があります。

>>> ## Find rides on route 22 on February 2, 2011
>>> target_date = "2011-02-02"
>>> target_route = "22"
>>>
>>> for row in rows:
...     if row['route'] == target_route and row['date'] == target_date:
...         print(f"Rides on route {target_route} on {target_date}: {row['rides']}")
...         break

まず、target_datetarget_route変数を定義します。次に、rowsリストの各行を反復処理します。各行について、routedateがターゲット値と一致するかどうかを確認します。一致するレコードが見つかったら、乗車人数を印刷し、目的のレコードを見つけたのでループを抜けます。

target_datetarget_route変数を変更することで、任意の日付の任意の路線を確認するようにこのコードを変更することができます。

質問3:各バス路線の総乗車人数はいくらですか?

各路線の総乗車人数を計算するためにCounterを使用しましょう。Countercollectionsモジュールからの辞書のサブクラスで、ハッシュ可能なオブジェクトをカウントするために使用されます。

>>> from collections import Counter
>>>
>>> ## Initialize a counter
>>> total_rides_by_route = Counter()
>>>
>>> ## Sum up rides for each route
>>> for row in rows:
...     total_rides_by_route[row['route']] += row['rides']
...
>>> ## View the top 5 routes by total ridership
>>> for route, rides in total_rides_by_route.most_common(5):
...     print(f"Route {route}: {rides:,} total rides")

まず、collectionsモジュールからCounterクラスをインポートします。次に、total_rides_by_routeという空のカウンターを初期化します。rowsリストの各行を反復処理する際に、各路線の乗車人数をカウンターに追加します。最後に、most_common(5)メソッドを使って、総乗車人数が最も多い上位5つの路線を取得し、結果を印刷します。

質問4:2001年から2011年までの10年間で乗車率の増加が最も大きかった5本のバス路線はどれですか?

これはより複雑なタスクです。各路線について、2001年の乗車率と2011年の乗車率を比較する必要があります。

>>> ## Create dictionaries to store total annual rides by route
>>> rides_2001 = Counter()
>>> rides_2011 = Counter()
>>>
>>> ## Collect data for each year
>>> for row in rows:
...     if row['date'].startswith('2001-'):
...         rides_2001[row['route']] += row['rides']
...     elif row['date'].startswith('2011-'):
...         rides_2011[row['route']] += row['rides']
...
>>> ## Calculate increases
>>> increases = {}
>>> for route in unique_routes:
...     if route in rides_2001 and route in rides_2011:
...         increase = rides_2011[route] - rides_2001[route]
...         increases[route] = increase
...
>>> ## Find the top 5 routes with the biggest increases
>>> import heapq
>>> top_5_increases = heapq.nlargest(5, increases.items(), key=lambda x: x[1])
>>>
>>> ## Display the results
>>> print("Top 5 routes with the greatest ridership increase from 2001 to 2011:")
>>> for route, increase in top_5_increases:
...     print(f"Route {route}: increased by {increase:,} rides")
...     print(f"  2001 rides: {rides_2001[route]:,}")
...     print(f"  2011 rides: {rides_2011[route]:,}")
...     print()

まず、2001年と2011年の各路線の総乗車人数をそれぞれ格納するために、2つのCounterオブジェクトrides_2001rides_2011を作成します。rowsリストの各行を反復処理する際に、日付が'2001 -'または'2011 -'で始まるかどうかを確認し、適切なカウンターに乗車人数を追加します。

次に、各路線の乗車率の増加を格納するための空の辞書increasesを作成します。一意の路線を反復処理し、各路線について2011年の乗車人数から2001年の乗車人数を引いて増加量を計算します。

増加量が最も大きい上位5つの路線を見つけるために、heapq.nlargest関数を使用します。この関数は、返す要素の数(この場合は5)、反復可能オブジェクト(increases.items())、および要素を比較する方法を指定するキー関数(lambda x: x[1])を受け取ります。

最後に、路線番号、乗車率の増加量、2001年と2011年の乗車人数を表示して結果を印刷します。

この分析により、10年間で乗車率の最も大きな増加を経験したバス路線が特定され、これは人口パターンの変化、サービスの改善、またはその他の興味深い傾向を示す可能性があります。

これらの分析を多くの方法で拡張することができます。たとえば、以下のことを行いたいかもしれません。

  • 曜日ごとの乗車率パターンを分析する
  • 乗車率が減少している路線を見つける
  • 乗車率の季節変動を比較する

この実験で学んだ技術は、このようなデータ探索と分析のための堅固な基礎を提供します。

✨ 解答を確認して練習

まとめ

この実験では、いくつかの重要なPythonのデータ操作技術を学びました。これには、CSVデータを辞書に読み込んで処理すること、データ変換にリスト内包表記、セット内包表記、辞書内包表記を使用すること、およびcollectionsモジュールからの特殊なコンテナ型を活用することが含まれます。また、これらのスキルを応用して有意義なデータ分析を行いました。

これらの技術はPythonによるデータ分析の基礎であり、さまざまな実世界のシナリオで役立ちます。データを処理、変換し、洞察を抽出する能力は、Pythonプログラマにとって非常に重要です。独自のデータセットを使って練習を続け、Pythonによるデータ分析の専門知識を高めましょう。