[머신러닝] 주식 종목 추천 시스템 - (2) 데이터 수집

2024. 8. 14. 19:05데이터 사이언스

yfinance 라이브러리와 pandas를 사용하여
S&P 500에 속한 회사들의 재무 정보를 수집하고
이를 데이터프레임으로 변환하는 과정에 대해 알아보겠습니다.


1. 라이브러리 임포트

import pandas as pd
import yfinance as yf
from tqdm import tqdm
  1. pandas: 데이터 조작 및 분석을 위한 라이브러리 → 특히 데이터프레임을 다루는 데 유용
  2. yfinance: 야후 파이낸스 데이터를 손쉽게 가져올 수 있게 해주는 라이브러리
  3. tqdm: 루프의 진행 상황을 시각적으로 보여주는 진행률 표시줄 라이브러리

DataFrame
  • DataFrame은 pandas 라이브러리에서 제공하는 2차원 데이터 구조로, 행(row)과 열(column)로 구성된 데이터 테이블
tqdm
  • 예시 코드
from tqdm import tqdm
import time

# 예제 작업: 100번의 반복 수행, 각 반복마다 0.1초 대기
for i in tqdm(range(100), desc="Processing"):
    time.sleep(0.1)
  • 출력 결과
Processing: 100%|████████████████████████████████| 100/100 [00:10<00:00,  9.99it/s]

 


2. Wikipedia에서 S&P 500 목록 가져오기

url = '<https://en.wikipedia.org/wiki/List_of_S%26P_500_companies>'
tables = pd.read_html(url)
sp500_table = tables[0]
  1. pd.read_html(url): 지정된 URL에서 HTML 테이블을 읽어 데이터프레임 리스트로 반환
    • 여기서는 S&P 500 목록이 포함된 Wikipedia 페이지를 사용
  2. tables[0]: 첫 번째 테이블을 선택 → S&P 500 기업 목록이 첫 번째 테이블에 있음

첫번째 테이블만 가져온 이유
import pandas as pd

# Wikipedia에서 S&P 500 목록 가져오기
url = 'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies'
tables = pd.read_html(url)

# 첫 번째 테이블
sp500_table_1 = tables[0]
print("첫 번째 테이블:")
print(sp500_table_1)

# 두 번째 테이블
sp500_table_2 = tables[1]
print("\n두 번째 테이블:")
print(sp500_table_2)

 

첫 번째 테이블과 두 번째 테이블 비교 분석

 

1. 첫 번째 테이블 분석 : S&P 500에 속한 기업들의 목록을 포함

  • Symbol: 주식의 티커 심볼(Ticker Symbol)
  • Security: 회사의 이름
  • GICS Sector: 회사가 속한 GICS(Global Industry Classification Standard) 섹터
  • GICS Sub-Industry: 회사가 속한 GICS 하위 산업
  • Headquarters Location: 회사의 본사 위치
  • Date added: 해당 회사가 S&P 500 지수에 추가된 날짜
  • CIK: SEC(미국 증권거래위원회)에 의해 부여된 고유 식별자(Central Index Key)
  • Founded: 회사가 설립된 연도두 번째 테이블 분석

2. 두 번째 테이블 분석 : S&P 500 구성 요소의 변경 내역을 포함

  • S&P 500 지수에서 어떤 회사가 추가되고, 어떤 회사가 제거되었는지에 대한 정보
  • Date Added: 회사가 S&P 500 지수에 추가된 날짜
  • Ticker: 새로 추가된 회사의 티커 심볼
  • Security: 새로 추가된 회사의 이름
  • Removed Date: 회사가 S&P 500 지수에서 제거된 날짜
  • Ticker: 제거된 회사의 티커 심볼
  • Security: 제거된 회사의 이름
  • Reason: 회사가 제거된 이유

3. S&P 500 티커 심볼 목록 생성

sp500_tickers = sp500_table['Symbol'].tolist()
  1. sp500_table['Symbol']: 데이터프레임의 'Symbol' 열을 선택
  2. .tolist(): 이 열을 리스트로 변환 → 각 항목은 S&P 500에 속한 기업의 티커 심볼

4. 가져올 재무 정보 목록

financial_fields = [
    "industry", "sector", "fullTimeEmployees", "auditRisk", "boardRisk",
    "compensationRisk", "shareHolderRightsRisk", "overallRisk", "dividendYield",
    "payoutRatio", "fiveYearAvgDividendYield", "beta", "trailingPE",
    "averageVolume", "marketCap", "currency", "enterpriseValue", "profitMargins",
    "heldPercentInsiders", "heldPercentInstitutions", "bookValue", "priceToBook",
    "pegRatio", "enterpriseToEbitda", "52WeekChange", "recommendationMean",
    "recommendationKey", "numberOfAnalystOpinions", "totalCash", "ebitda",
    "totalDebt", "quickRatio", "currentRatio", "totalRevenue", "returnOnAssets",
    "returnOnEquity", "grossProfits", "freeCashflow", "operatingCashflow",
    "earningsGrowth", "revenueGrowth", "grossMargins", "ebitdaMargins",
    "operatingMargins"
]
  • financial_fields: 수집할 재무 정보의 목록을 정의 → 이 목록은 딕셔너리의 키로 사용됨

5. 데이터 저장을 위한 리스트 초기화

financial_data = []
  • financial_data: 각 기업의 재무 정보를 저장할 빈 리스트

6. 각 종목의 재무 정보 수집

for ticker in tqdm(sp500_tickers, desc="Fetching financial data"):
    stock = yf.Ticker(ticker)
    info = stock.info

    # 필요한 재무 정보 추가
    data = {'Ticker': ticker}
    for field in financial_fields:
        data[field] = info.get(field)

    financial_data.append(data)

 

for ticker in tqdm(sp500_tickers, desc="Fetching financial data")
  • tqdm을 사용하여 진행률 표시줄을 출력하면서 sp500_tickers 리스트(S&P 500 기업 목록)의 각 티커 심볼에 대해 루프를 수행
  • 첫 번째 인수는 진행률 표시줄을 표시할 이터러블 객체
  • 두 번째 인수는 진행률 표시줄의 설명(description) 텍스트를 설정하는 매개변수
stock = yf.Ticker(ticker)
  • yfinance 라이브러리의 Ticker 클래스를 사용하여 각 티커에 대한 Ticker 객체를 생성
  • Ticker 객체를 생성하는 이유
    • Ticker 객체를 생성하면 해당 티커(symbol)와 관련된 모든 데이터를 손쉽게 접근할 수 있다.
    • (ex)  stock.info, stock.history(), stock.financials 등 다양한 속성과 메서드를 사용할 수 있다.
info = stock.info : 해당 티커에 대한 주식의 주요 재무 정보를 담고 있는 딕셔너리를 반환

 

data = {'Ticker': ticker}: 현재 티커를 포함하는 딕셔너리를 초기화
  • 초기화된 상태의 예시
# 예시로 사용할 S&P 500 티커 심볼 목록
sp500_tickers = ['AAPL', 'MSFT', 'GOOGL']  # 간단한 예시로 일부만 포함

# 각 종목의 재무 정보 수집
for ticker in sp500_tickers:
    data = {'Ticker': ticker}  # 현재 티커를 포함하는 딕셔너리 초기화
    print(f"Data for {ticker} after initialization: {data}")
  • 출력 결과
Data for AAPL after initialization: {'Ticker': 'AAPL'}
Data for MSFT after initialization: {'Ticker': 'MSFT'}
Data for GOOGL after initialization: {'Ticker': 'GOOGL'}

 

 

for field in financial_fields: 수집할 재무 정보 목록을 순회

 

data[field] = info.get(field): 재무 정보를 딕셔너리에 추가
  • info.get(field)는 해당 키가 존재하지 않을 경우 None을 반환하여 에러를 방지
  • 재무 정보가 추가된 딕셔너리 예시
  • dic = { 'Ticker': 'AAPL', 'industry': 'Consumer Electronics', 'sector': 'Technology', 'fullTimeEmployees': 147000, 'beta': 1.2, 'trailingPE': 28.5, 'marketCap': 2200000000000 ... }
financial_data.append(data): 수집된 재무 정보를 리스트에 추가

7. 데이터프레임으로 변환

df = pd.DataFrame(financial_data)
  • pd.DataFrame(financial_data): 수집된 재무 정보 리스트를 데이터프레임으로 변환

데이터 결합

combined_df = pd.concat([df, sp500_table['Security']], axis=1)
pd.concat : 두 데이터프레임을 결합
[df, sp500_table['Security']]는 결합할 데이터프레임 목록
  • 여기서 df는 기존의 재무 데이터를 포함한 데이터프레임
Ticker                    industry               sector  fullTimeEmployees  beta  trailingPE       marketCap
0   AAPL       Consumer Electronics             Technology            147000   1.2        28.5  2200000000000
1   MSFT                   Software             Technology            181000   0.8        35.2  1850000000000
2  GOOGL  Interactive Media & Services  Communication Services            135301   1.1        30.4  1500000000000
  • sp500_table['Security']는 S&P 500 기업 이름을 포함하는 시리즈
0              Apple Inc.
1    Microsoft Corporation
2            Alphabet Inc.
Name: Security, dtype: object
  • axis=1은 열(column)을 기준으로 결합하겠다는 의미 → 즉, 기존 데이터프레임 df의 오른쪽에 sp500_table['Security'] 열을 추가
Ticker                    industry               sector  fullTimeEmployees  beta  trailingPE       marketCap              Security
0   AAPL       Consumer Electronics             Technology            147000   1.2        28.5  2200000000000           Apple Inc.
1   MSFT                   Software             Technology            181000   0.8        35.2  1850000000000  Microsoft Corporation
2  GOOGL  Interactive Media & Services  Communication Services            135301   1.1        30.4  1500000000000          Alphabet Inc.

열 순서 재정렬

열 이름 가져오기
columns = list(combined_df.columns)
  • combined_df.columns는 결합된 데이터프레임의 모든 열 이름을 반환
  • list()를 사용하여 열 이름을 리스트로 변환
Security 열 재정렬
security_col = columns.pop()
  • columns.pop()은 리스트 columns의 마지막 요소를 제거하고, 그 값을 security_col 변수에 저장
    • 여기서는 sp500_table['Security']가 결합 후의 마지막 열이라고 가정하고, 그 열 이름을 리스트에서 제거하고 저장
columns.insert(1, security_col)
  • columns.insert(1, security_col)는 security_col을 리스트의 두 번째 위치(인덱스 1)에 삽입 → 이렇게 하면 Security 열이 Ticker 열 다음에 오게 됨
열 순서 조정
df = combined_df[columns]
  • combined_df[columns]는 columns 리스트에 정의된 순서대로 열을 정렬한 새로운 데이터프레임을 반환 → 이를 df 변수에 다시 저장하여, 열 순서가 재정렬된 최종 데이터프레임을 얻음

출력 결과 예시

  Ticker            Security                    industry               sector  fullTimeEmployees  beta  trailingPE       marketCap
0   AAPL           Apple Inc.       Consumer Electronics             Technology            147000   1.2        28.5  2200000000000
1   MSFT  Microsoft Corporation                   Software             Technology            181000   0.8        35.2  1850000000000
2  GOOGL          Alphabet Inc.  Interactive Media & Services  Communication Services            135301   1.1        30.4  1500000000000
  • 이 출력 결과에서 Security 열이 Ticker 열 다음에 위치하도록 재정렬