1. Daum 증권 뉴스 내용 크롤링(by selenium)
Selenium이란 #gpt 피셜
1. 웹 자동화 도구: Selenium은 웹 브라우저를 자동으로 제어할 수 있는 오픈 소스 도구이다.
사용자가 브라우저에서 수행하는 작업을 프로그램 코드로 자동화할 수 있게 해줌
2. 테스트 자동화: 주로 웹 애플리케이션의 테스트를 자동화하는 데 사용
예를 들어 로그인 기능을 테스트하거나 폼을 자동으로 제출하는 등의 작업을 코드로 작성할 수 있음
위의 말처럼 내 의지가 아닌 자동으로 작동하는(코드를 설정한 대로) 크롬 페이지를 하나 띄워서 하고싶은 작업을 하는것.
자동화를 해놓으면 반복적인 일을 자동으로 할 수 있어 간편하다.
활용 예시:
selenium 을 이용해 Daum 해외증시 뉴스 페이지에 접속, 각 뉴스에 들어가 뉴스 내용을 Xpath를 통해 텍스트로 추출하고, 해당 내용을 DataFrame에 넣고, 해당 데이터프레임을 제목과 함께 엑셀로 저장한다.
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import pandas as pd
import time
# 크롬 드라이버 설정
driver = webdriver.Chrome(service=Service()) #service=Service(ChromeDriverManager().install())가 있었는데 크롬드라이버 업데이트로 지우니까 해결
# 페이지 열기
driver.get('https://finance.daum.net/global/news#?page=1')
driver.implicitly_wait(10)
# 뉴스 제목과 링크를 저장할 리스트
news_data = []
# 각 기사의 제목을 크롤링
news_items = driver.find_elements(By.XPATH, '//*[@id="boxNews"]/div[2]/div/ul/li/span/a[1]')
for i, item in enumerate(news_items):
title = item.text
item.click()
driver.implicitly_wait(10)
# 현재 페이지의 URL 추출
link = driver.current_url
# 데이터 저장
news_data.append({'Title': title, 'Link': link})
# 이전 페이지로 돌아가기
driver.back()
driver.implicitly_wait(10)
# 뉴스 항목을 다시 찾기
news_items = driver.find_elements(By.XPATH, '//*[@id="boxNews"]/div[2]/div/ul/li/span/a[1]')
# 드라이버 종료
driver.quit()
# 데이터프레임 생성
df = pd.DataFrame(news_data)
# 엑셀 파일로 저장할 경로 지정
output_path = 'C:/Users/sajog/Desktop/뉴스크롤링/뉴스크롤링.xlsx'
# 엑셀 파일로 저장
df.to_excel(output_path, index=False)
print(f"엑셀 파일이 성공적으로 {output_path}에 저장되었습니다.")
제목과 링크를 추출하여 엑셀로 저장한 것
여기에서 멈추면 심심하니 크롤링한 내용을 데이터프레임으로 저장하고, 저장한 내용을 gpt api를 이용해서 본문 요약을 시킨다. 해당 내용을 엑셀에 저장한다.
엑셀에는 제목, 요약내용, 링크 방식으로 출력시킨다. 영어를 조금이라도 사용하고자 gpt에게 질문을 영어로 하여 영어 요약으로 출력하는 방식을 채택했다.
from openai import OpenAI
import pandas as pd
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
import os
# OpenAI API key configuration (fetch from environment variable or set directly)
client = OpenAI(
api_key=os.environ['OPENAI_API_KEY'], # 윈도우 환경변수에 api추가하고 os.environ통해서 지정
)
def summarize_content(content):
if not content.strip():
return None # No content to summarize
try:
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a skillful news summarizer."}, #한글로 요약하고싶으면 한글로 질문
{"role": "user", "content": f"Please summarize the following article:\n\n{content}\n\nSummary:"}
],
max_tokens=150,
temperature=0.5,
)
summary = response.choices[0].message.content
return summary if summary else None
except Exception as e:
print(f"Error during OpenAI API call: {e}")
return None
# Chrome WebDriver setup
#Survice() 메소드 안에 ChromeDriverManager().install()를 넣었었는데 크롬 업데이트하면서 넣으면 코드가 안돌아감
driver = webdriver.Chrome(service=Service())
driver.implicitly_wait(10)
# List to store news data
news_data = []
for page in range(1, 5): # Crawl pages 1 to 4
driver.get(f'https://finance.daum.net/global/news#?page={page}')
# Wait for news items to load
wait = WebDriverWait(driver, 10)
news_items = wait.until(
EC.presence_of_all_elements_located((By.XPATH, '//*[@id="boxNews"]/div[2]/div/ul/li/span/a[1]'))
)
for item in news_items:
try:
title = item.text
link = item.get_attribute('href')
item.click()
# Wait for article content to load
wait.until(
EC.presence_of_element_located((By.XPATH, '//*[@id="mArticle"]/div[2]/div[2]/section'))
)
# Extract article content
content_element = driver.find_element(By.XPATH, '//*[@id="mArticle"]/div[2]/div[2]/section')
content = content_element.text
# Summarize the article content
summary = summarize_content(content)
if summary: # Only save data if a summary is available
news_data.append({'Title': title, 'Summary': summary, 'Link': link})
# Navigate back to the previous page
driver.back()
# Reload the list of news items
news_items = wait.until(
EC.presence_of_all_elements_located((By.XPATH, '//*[@id="boxNews"]/div[2]/div/ul/li/span/a[1]'))
)
except Exception as e:
print(f"Error occurred: {e}")
continue
# Close the driver
driver.quit()
# Create DataFrame from the news data
df_new = pd.DataFrame(news_data)
# Define the file path and sheet name for the Excel file
file_path = 'C:/Users/sajog/Desktop/뉴스크롤링/뉴스크롤링.xlsx'
# 엑셀파일로 저장하기
with pd.ExcelWriter(file_path, mode='a', engine='openpyxl', if_sheet_exists='replace') as writer:
df_new.to_excel(writer, index=False, sheet_name='Sheet1')
print(f"Excel file successfully saved at {file_path}.")
2. 수송계획법 풀기(by linporg, numpy, haversine)
2024년 field camp에서 다뤘던 문제로 각 공장에서 수요지로 가는 수송문제를 풀때 scipy 모듈의 linprog 함수를 통해서 수송비용의 최소 목적함수 값을 구한다.
문제에서는 각 공장의 위,경도와 해상, 육상의 거리별 단가가 주어저 haversine함수를 통해서 각 수송지까지의 거리를 구한 뒤, 거리별 금액을 곱해 최종 최소의 수송비용을 구한다.
from scipy.optimize.linprog import linporg 의미:(from gpt)
SciPy 라이브러리
SciPy: 과학적 계산을 위한 파이썬 라이브러리입니다. 선형대수, 미적분, 통계, 최적화 등의 다양한 기능을 제공합니다.
optimize 모듈
scipy.optimize: 최적화 문제를 해결하는 다양한 도구를 제공하는 SciPy의 하위 모듈입니다. 여기에는 선형 계획법, 비선형 최적화, 곡선 맞춤 등의 알고리즘이 포함됩니다.
linprog 함수
linprog: 선형 계획법(Linear Programming) 문제를 풀기 위한 함수입니다. 주어진 목적 함수를 최소화하거나 최대화하는 최적의 변수 값을 찾습니다.
import pandas as pd
from haversine import haversine, Unit
from scipy.optimize import linprog
import numpy as np
# 데이터프레임 생성
supply_data = {
"생산 공장": ["HMI", "HAOS", "BHMC", "HMMA", "HMMC", "HMB", "HMMI", "HTBC", "Vietnam", "Korea"],
"위도": [12.9992, 40.7724, 39.9689, 32.3887, 49.6624, -22.6848, -6.404, 31.118, 21.346, 36.7925],
"경도": [79.9906, 30.0244, 116.3194, -86.3544, 18.396, -47.6035, 107.186, 104.184, 105.61, 126.9928]
}
port_data = {
"미국 항구": ["시애틀 항", "로스앤젤레스 항", "휴스턴 항", "찰스턴 항", "뉴욕 뉴저지 항"],
"위도": [47.3622, 33.74, 29.7426, 32.7812, 40.6688],
"경도": [-122.1955, -118.2727, -95.2622, -79.9283, -74.016]
}
demand_data = {
"수요지": ["시카고", "LA", "워싱턴 DC", "미시간", "뉴욕", "캘리포니아", "몬타나", "플로리다"],
"위도": [41.8666, 36.1495, 38.8221, 44.7375, 40.7576, 36.8192, 45.7338, 27.9837],
"경도": [-87.624, -115.249, -77.0575, -85.5944, -73.7811, -119.7898, -108.6091, -81.679]
}
supply_df = pd.DataFrame(supply_data)
port_df = pd.DataFrame(port_data)
demand_df = pd.DataFrame(demand_data)
# 공급지에서 미국 항구로 가는 거리 및 비용 계산
supply_to_port_cost = np.zeros((len(supply_df), len(port_df)))
for i, supply in supply_df.iterrows():
for j, port in port_df.iterrows():
distance = haversine((supply['위도'], supply['경도']), (port['위도'], port['경도']), unit=Unit.KILOMETERS)
cost = 0.05 * distance
supply_to_port_cost[i, j] = cost
supply_to_port_cost_df = pd.DataFrame(supply_to_port_cost, index=supply_df['생산 공장'], columns=port_df['미국 항구'])
# 미국 항구에서 수요지로 가는 거리 및 비용 계산
port_to_demand_cost = np.zeros((len(port_df), len(demand_df)))
for i, port in port_df.iterrows():
for j, demand in demand_df.iterrows():
distance = haversine((port['위도'], port['경도']), (demand['위도'], demand['경도']), unit=Unit.KILOMETERS)
cost = 0.2 * distance
port_to_demand_cost[i, j] = cost
port_to_demand_cost_df = pd.DataFrame(port_to_demand_cost, index=port_df['미국 항구'], columns=demand_df['수요지'])
# 공장에서 수요지까지의 최소 비용 계산
factory_to_demand_cost = np.zeros((len(supply_df), len(demand_df)))
for i, supply in supply_df.iterrows():
for j, demand in demand_df.iterrows():
min_cost = float('inf')
for k, port in port_df.iterrows():
cost = supply_to_port_cost_df.loc[supply['생산 공장'], port['미국 항구']] + port_to_demand_cost_df.loc[port['미국 항구'], demand['수요지']]
if cost < min_cost:
min_cost = cost
factory_to_demand_cost[i, j] = min_cost
factory_to_demand_cost_df = pd.DataFrame(factory_to_demand_cost, index=supply_df['생산 공장'], columns=demand_df['수요지'])
각 공급지와 수요지까지의 최소비용을 구했으니 이제 각 공장과 수요지의 차종별 공급량, 수요량에 따라서 수송계획을 짜야한다.
# 공급량 데이터
supply = {
("HMI", "Elantra"): 23000, ("HMI", "Sonata"): 8394, ("HMI", "Palisade"): 17019, ("HMI", "Tucson"): 20593,
("HAOS", "Kona"): 22587, ("HAOS", "Venue"): 5973, ("HAOS", "Nexo"): 70,
("BHMC", "Santa Cruz"): 6654, ("BHMC", "Sonata"): 8436, ("BHMC", "IONIQ 5"): 6581, ("BHMC", "IONIQ 6"): 3070,
("HMMA", "Tucson"): 25428, ("HMMA", "Santa-Fe"): 17199, ("HMMA", "GV70"): 4812, ("HMMA", "GV70 EV"): 828,
("HMMC", "Elantra"): 27273, ("HMMC", "Kona"): 17048, ("HMMC", "Tucson"): 32544, ("HMMC", "Palisade"): 17330,
("HMB", "GV60"): 989, ("HMB", "GV70"): 3493, ("HMB", "GV70 EV"): 698, ("HMB", "GV80"): 4248,
("HMMI", "Santa-Fe"): 29620, ("HMMI", "Santa Cruz"): 8322, ("HMMI", "Sonata"): 8382, ("HMMI", "Palisade"): 10195, ("HMMI", "Venue"): 4854,
("HTBC", "G70"): 2769, ("HTBC", "G80 (RG3)"): 788, ("HTBC", "G80 (RG3 EV)"): 124, ("HTBC", "G90"): 206,
("Vietnam", "IONIQ 5"): 8374, ("Vietnam", "IONIQ 5 Robo Taxi"): 18, ("Vietnam", "IONIQ 6"): 2928,
("Korea", "GV80"): 4063, ("Korea", "G80 (RG3)"): 889, ("Korea", "G70"): 1889, ("Korea", "G90"): 406
}
# 수요량
demand = {
("시카고", "Elantra"): 10719, ("시카고", "Sonata"): 7576, ("시카고", "Kona"): 17659, ("시카고", "G90"): 262, ("시카고", "Venue"): 4008,
("LA", "Sonata"): 4642, ("LA", "G70"): 1050, ("LA", "Venue"): 2900, ("LA", "Kona"): 12209, ("LA", "Tucson"): 30908, ("LA", "GV70"): 2673,
("워싱턴 DC", "Sonata"): 714, ("워싱턴 DC", "Kona"): 9767, ("워싱턴 DC", "Santa Cruz"): 4818, ("워싱턴 DC", "Nexo"): 36, ("워싱턴 DC", "IONIQ 5"): 6134, ("워싱턴 DC", "Santa-Fe"): 15658, ("워싱턴 DC", "Palisade"): 15098, ("워싱턴 DC", "GV60"): 346,
("미시간", "Sonata"): 1330, ("미시간", "G70"): 2104, ("미시간", "G90"): 350, ("미시간", "IONIQ 5 Robo Taxi"): 18, ("미시간", "Santa-Fe"): 14609, ("미시간", "Palisade"): 10860, ("미시간", "GV60"): 643, ("미시간", "GV70"): 1704,
("뉴욕", "Sonata"): 6690, ("뉴욕", "G70"): 1504, ("뉴욕", "Nexo"): 34, ("뉴욕", "IONIQ 6"): 2143, ("뉴욕", "GV70"): 2104, ("뉴욕", "GV70 EV"): 1526, ("뉴욕", "GV80"): 2555,
("캘리포니아", "Elantra"): 13837, ("캘리포니아", "G80 (RG3 EV)"): 124, ("캘리포니아", "Tucson"): 22374, ("캘리포니아", "IONIQ 5"): 8821, ("캘리포니아", "IONIQ 6"): 2123, ("캘리포니아", "GV70"): 1824,
("몬타나", "Elantra"): 17461, ("몬타나", "Sonata"): 3145, ("몬타나", "G80 (RG3)"): 714, ("몬타나", "Tucson"): 10568, ("몬타나", "Santa Cruz"): 4474, ("몬타나", "IONIQ 6"): 1732, ("몬타나", "Palisade"): 7523, ("몬타나", "GV80"): 1912,
("플로리다", "Elantra"): 8256, ("플로리다", "Sonata"): 1115, ("플로리다", "G80 (RG3)"): 963, ("플로리다", "Venue"): 3919, ("플로리다", "Tucson"): 14715, ("플로리다", "Santa Cruz"): 5684, ("플로리다", "Santa-Fe"): 16552, ("플로리다", "Palisade"): 11063, ("플로리다", "GV80"): 3844
}
# 공급량 데이터를 데이터프레임으로 변환
supply_list = [(factory, model, quantity) for (factory, model), quantity in supply.items()]
supply_df = pd.DataFrame(supply_list, columns=["생산공장", "Model", "공급량"])
# 수요량 데이터를 데이터프레임으로 변환
demand_list = [(location, model, quantity) for (location, model), quantity in demand.items()]
demand_df = pd.DataFrame(demand_list, columns=["수요지", "Model", "수요량"])
# 고유한 Model
unique_models=['Elantra',
'Sonata',
'Palisade',
'Kona',
'Venue',
'Nexo',
'Santa Cruz',
'IONIQ 5',
'IONIQ 6',
'IONIQ 5 Robo Taxi',
'Tucson',
'Santa-Fe',
'GV70',
'GV70 EV',
'GV60',
'GV80',
'G70',
'G80 (RG3)',
'G80 (RG3 EV)',
'G90']
# 최적화 함수 정의
def optimize_transportation(model):
# 특정 모델에 대해 생산공장 및 수요지 필터링
model_supply_df = supply_df[supply_df['Model'] == model]
model_demand_df = demand_df[demand_df['Model'] == model]
num_supply = len(model_supply_df)
num_demand = len(model_demand_df)
# 비용 벡터 생성
c = factory_to_demand_cost_df.loc[model_supply_df['생산공장'], model_demand_df['수요지']].values.flatten()
# 제약 조건 생성
A_eq = []
b_eq = []
# 공급지에서 수요지로 가는 제약 조건 (공급량)
for supply_idx in range(num_supply):
row = [0] * (num_supply * num_demand)
for demand_idx in range(num_demand):
row[supply_idx * num_demand + demand_idx] = 1
A_eq.append(row)
b_eq.append(model_supply_df['공급량'].iloc[supply_idx])
# 수요지에서 수요량을 충족하는 제약 조건
for demand_idx in range(num_demand):
row = [0] * (num_supply * num_demand)
for supply_idx in range(num_supply):
row[supply_idx * num_demand + demand_idx] = 1
A_eq.append(row)
b_eq.append(model_demand_df['수요량'].iloc[demand_idx])
A_eq = np.array(A_eq)
b_eq = np.array(b_eq)
# 경계 조건 설정
bounds = [(0, None) for _ in range(len(c))]
# 선형 계획법 문제 풀기
res = linprog(c, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method='highs')
# 결과 출력 및 총 비용 계산
if res.success:
print(f"모델 {model}의 최적해를 찾았습니다.")
print(f"모델 {model}의 목적 함수 값:", res.fun)
# 최적해 출력
x = res.x.reshape((num_supply, num_demand))
solution_df = pd.DataFrame(x, index=model_supply_df['생산공장'], columns=model_demand_df['수요지'])
# 전체 위치를 포함한 데이터프레임 생성
full_solution_df = pd.DataFrame(0, index=supply_df['생산공장'].unique(), columns=demand_df['수요지'].unique())
for supply in model_supply_df['생산공장']:
for demand in model_demand_df['수요지']:
full_solution_df.loc[supply, demand] = solution_df.loc[supply, demand]
# 총 공급량 및 총 수요량 추가
full_solution_df["생산량"] = full_solution_df.sum(axis=1)
total_demand = full_solution_df.sum(axis=0)
total_demand.name = "수요량"
full_solution_df = pd.concat([full_solution_df, total_demand.to_frame().T])
print("\n최적해 (공장에서 수요지로의 수송량):")
print(full_solution_df)
# 총 생산대수
total_production = full_solution_df["생산량"].loc["수요량"]
# 결과 저장
results.append([model, total_production, res.fun])
return res.fun, full_solution_df
else:
print(f"모델 {model}의 최적해를 찾지 못했습니다.")
return None
# 특정 모델 최적화 실행 예제
model_name = "Elantra"
optimize_transportation(model_name)
이제 이걸 for 반복문을 통해 모든 차량에 대해서 실시한다.
각 차량마다 공급량, 수요량이 나왔으니 차량의 가격을 곱하면 전체 판매 가격이 나온다. 거기에서 순이익률을 통해 차량을 팔아서 얻는 순 매출액을 계산하고 거기에 거리로 인한 수송비용을 빼면 최종 순이익이 나온다.
각 차량을 계산한 것을 데이터프레임으로 저장하면
# 판매 데이터 (이미지 기반)
sales_data = {
"Model": ["Elantra", "Sonata", "Palisade", "Kona", "Venue", "Nexo", "Santa Cruz", "IONIQ 5", "IONIQ 6",
"IONIQ 5 Robo Taxi", "Tucson", "Santa-Fe", "GV70", "GV70 EV", "GV60", "GV80", "G70",
"G80 (RG3)", "G80 (RG3 EV)", "G90"],
"판매 가격 (USD)": [20000, 25000, 35000, 30000, 20000, 60000, 28000, 45000, 47000, 55000, 27000, 32000,
50000, 65000, 60000, 70000, 45000, 50000, 70000, 80000],
"순이익율 (%)": [5, 6, 10, 10, 5, 10, 7, 12, 12, 12, 6, 7, 12, 15, 15, 12, 10, 10, 15, 12],
"전기차 유무": ["X", "X", "X", "O", "X", "O", "X", "O", "O", "O", "X", "X", "X", "O", "O", "X", "X", "X", "O", "X"]
}
sales_df = pd.DataFrame(sales_data)
# 1대당 순이익 계산
sales_df["1대당 순이익"] = sales_df["판매 가격 (USD)"] * (sales_df["순이익율 (%)"] / 100)
# 판매 데이터와 최적화 결과 데이터 병합
merged_df = pd.merge(results_df, sales_df, on="Model")
# 총 이익 계산
merged_df["profit"] = merged_df["생산대수"] * merged_df["1대당 순이익"]
# 순이익에서 운송 비용을 뺀 값 계산
merged_df["Net Profit After Transportation Cost: Total Profit - Z"] = merged_df["profit"] - merged_df["Minimized Cost: Z"]
# 병합된 데이터프레임 출력
print("병합된 데이터프레임:")
merged_df
Minimized Cost: Z 는 해당 차종을 수송하기 위해 필요한 최소 수송금액이고, profit은 순이익률을 계산한 순 매출액, Net Profit은 거기에 수송비용을 제외한 순 이익이다.
필드캠프에서는 여기에서 조건들을 추가하여 변화하는 값을 계산했지만 여기에서는 이정도만 정리하고 나중에 따로 올릴 예정이다.
3. HTML, Web 내용 크롤링하기(by Beautifulsoup, pandas, request)
사실 1번이랑 조금 겹치는감이 없지않아 있지만 할줄아는게 크롤링이라 이걸로한다.
빈 데이터프레임을 생성하고, for 구문을 통해 각 페이지를 순회하며 soup.find('span', class_='p-table__subject_text') 를 통해서 원하는 정보가 있는 Xpath를 찾아 경로를 입력한다. 해당 경로는 페이지에서 f12를 통해 찾을 수 있다.
bs4 라이브러리: 웹 페이지에서 원하는 데이터를 추출할때 사용하는 라이브러. Beautifulsoup4 클래스의 주요 클래스
Beautifulsoup 클래스: HTML 문서를 파싱하고, 태그나 속성, 텍스트 등을 탐색할 수 있는 다양한 메서드를 제공.
이를 사용해 HTML 구조를 나무(tree) 구조로 변환하고, 이 구조를 통해 요소를 쉽게 찾을 수 있다.
request: Python에서 HTTP 요청을 보내고, 웹 서버와 상호작용하기
이 라이브러리는 웹에서 데이터를 가져오거나 서버와 상호작용하는 작업을 쉽게 할 수 있도록 도움.
import requests
from bs4 import BeautifulSoup
import pandas as pd
# 전체 페이지 수
num_pages = 11
# 모든 페이지의 데이터를 저장할 빈 리스트 생성
data = []
# 모든 페이지에 대해 반복
for i in range(1, 12):
url = f'https://lifestudy.yangcheon.go.kr/sugang/prgm/lctre/list.do?menuNo=300001&pageIndex={i}&pageUnit=10&choseong=&searchLctreTrgtCdArr=&searchSeCd1Arr=&searchDayWCdArr=&searchSttusCdArr=&searchFreeAtArr=&searchType=lctre&searchWrd=&eclstTyCd=ET_01&eclstNo=3&dongCd1=&dongCd2=' # 페이지 번호에 따라 URL 생성
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
table = soup.find('div', class_='bd-list').find('table')
# 각 행에서 데이터 추출
for row in table.find_all('tr')[1:]:
cells = row.find_all('td')
course_name = cells[2].text.strip()
enrollment = cells[5].text.strip()
data.append([course_name, enrollment])
# 데이터프레임 생성
df = pd.DataFrame(data, columns=['강좌명', '접수인원(신청/정원/대기)'])
# 결과 출력
print(df)
# 엑셀 파일로 저장
df.to_excel('강좌_접수인원4.xlsx', index=False)
'파이썬 머신러닝 판다스 데이터분석' 카테고리의 다른 글
4주차- 머신러닝 데이터분석 (0) | 2024.08.25 |
---|---|
4주차- 데이터프레임의 다양한 응용 (0) | 2024.08.22 |
3주차-데이터 사전 처리 (0) | 2024.08.21 |
3주차-시각화 도구 및 데이터 사전 처리 (0) | 2024.08.21 |
2주차- 데이터 살펴보기 (0) | 2024.08.14 |