본문 바로가기
study/Python

[Python] 41. 데이터전처리, 날짜데이터처리, 주식데이터처리, BeautifulSoup

by 금이패런츠 2022. 6. 30.
728x90
반응형

데이터전처리

날짜데이터처리

주식데이터처리

BeautifulSoup

# -*- coding: utf-8 -*-
"""
Created on Thu Jun 30 09:32:18 2022

@author: p011v
"""
'''
    데이터 전처리 : 원본데이터를 원하는 형태로 변경하는 과정.
        1. 결측값 처리 : 값이 없는 경우.
            - isnull() : 결측값인 경우 True, 일반값인 경우 False
            - notnull() : 결측값인 경우 False, 일반값인 경우 True
            - dropna() :
                dropna(axis=1, thresh=500) :결측값이 500개 이상인 컬럼 제거
                dropna(subset=["컬럼명"], how="any"/"all", axis=0, inplace=True) : 결측값을 가지고 있는 행 제거
            - fillna : 결측값 치환
                fillna(치환값, inplace=True)
                fillna(방법,inplace=True) : method="ffill"/"bfill"
        2. 중복데이터 처리
            - duplicated() : 중복데이터 찾기. 첫번째 데이터는 False, 같은 데이터인 경우 두번째 True
            - drop_duplicates() : 중복데이터를 제거. 중복된 데이터 중 한개는 남김.
        3. 새로운 컬럼 생성
            - df[컬럼명] : 컬럼명이 없는 경우 생성, 있으면 수정
            - round(자릿수) : 반올림.
        4. 오류데이터 존재
            - 결측값 치환 : 결측값 (np.nan)
                        replace(오류문자열, np.nan, inplace=True)
'''

import pandas as pd
import numpy as np
df = pd.read_csv('data/auto-mpg.csv')
df.info()
df["horsepower"].unique()
# ? 인 값을 결측값으로 변환
df["horsepower"].replace("?", np.nan, inplace=True)
df["horsepower"][df["horsepower"].isnull()]
# 결측값이 있는 행을 삭제하기
df.dropna(subset=["horsepower"], how="any", axis=0, inplace=True)
df.info()
# horsepower 컬럼의 자료형을 실수형으로 변환
#astype("자료형"): 요소들의 자료형을 변환 함수
df["horsepower"] = df["horsepower"].astype("float")
df.info()
df['horsepower'].describe()

#origin컬럼: 생산지역. 1:USA, 2:EU, 3:JAPAN
df['origin'].unique()
#정수형 컬럼을 문자열형 범주형 데이터로 변환.
#범주형: category형
#1. 정수형데이터를 문자열형으로 변환. 1=> USA, 2=> EU, 3=>JAPAN
df['origin'].replace({1:'USA', 2:'EU', 3:'JAPAN'}, inplace=True)
df['origin'].unique()
df.info()
#2. 문자열형을 범주형으로 변환
df['origin'] = df['origin'].astype('category')
df.info()
#3. 범주형을 문자열형으로 변환
df['origin'] = df['origin'].astype('str')
df.info()

# 범주형: 값의 범위를 가지고 있는 자료형. 데이터가 크기와 상관없이 값의 범위 의미 (예) 학년.. 1.2학년..이런거없음
# 정수형 category 자료형
age = pd.Series([26,42,27,25,20,20,21,22,23,25])
stu_class = pd.Categorical([1,1,2,2,2,3,3,4,4,4])
# 문자형 category 자료형
gender = pd.Categorical(['F','M','M','M','M','F','F','F','M','M'])
c_df = pd.DataFrame({'age':age, 'class': stu_class, 'gender': gender})
c_df.info()
c_df.describe()
# class 컬럼을 범주형 => 정수형
c_df['class'] = c_df['class'].astype('int64')
c_df.info()
c_df.describe()

# 날짜 데이터 처리
# freq='D' : 일자기준. 기본값 (생략가능)
# freq='MS' : 월의 시작일
# freq='M' : 월의 종료일
# freq='3M' :분기의 종료일
# dates = pd.date_range('20220101',periods=6, freq='D')
dates = pd.date_range('20220101', periods=6, freq='D')
dates
df = pd.DataFrame({'count1':[10,2,3,1,5,1],
                   'count2':[1,2,3,6,7,8]}, index=dates)
df
df.info()

dates = pd.date_range('20220110', periods=6, freq='MS')
dates
dates = pd.date_range('20220110', periods=6, freq='M')
dates
dates = pd.date_range('20220110', periods=6, freq='3M')
dates
dates = pd.date_range('20220110', periods=6, freq='6M')
dates

# 주식데이터 처리
df=pd.read_csv('data/stock-data.csv')
df.info()
df
# 문자열 형태의 date 컬럼을 datetime형으로 새컬럼 생성하기
df["new_Date"] = pd.to_datetime(df['Date'])
df.info()
# new_Date 컬럼에서 년,월,일 정보를 각각 컬럼으로 생성하기
df['Year'] = df['new_Date'].dt.year
df['Month'] = df['new_Date'].dt.month
df['Day'] = df['new_Date'].dt.day
df.info()
# 월별 평균값을 조회하기
df.groupby("Month").mean()[["Close", "Start", "Volume"]]
# datetime 형태의 new_Date컬럼을 문자열형으로 "연월일"컬럼 생성하기
df['연월일'] = df['new_Date'].astype("str")
df.info()
df["연월일", "Date"]

# 연월일 컬럼을 이용하여 년,월,일 문자열형 컬럼을 생성하기
dates = df["연월일"].str.split("-")
dates
df["연"] = dates.str.get(0)
df["월"] = dates.str.get(1)
df["일"] = dates.str.get(2)
df[["연월일", "연", "월", "일", "Month"]]
# 월별 평균값을 조회하기
df.groupby("월").mean()[["Close", "Start", "Volume"]]

# 필터 : 조건에 맞는 데이터 조회
import seaborn as sns
titanic = sns.load_dataset("titanic")
# 타이타닉 승객중 10대(10~19세)인 승객만 조회하기 df_teenage 데이터에 저장하기
df_teenage = titanic.loc[(titanic.age >= 10) & (titanic.age < 20)]
df_teenage["age"]

# 타이타닉 승객중 10세미만의 여성 승객만 조회하기 df_female_under10 데이터에 저장하기
df_female_under10 = titanic.loc[(titanic.sex == "female") & (titanic.age < 10)]
df_female_under10[["age", "sex"]]

titanic.sibsp.unique()
# 동행자(sibsp)의 수가 3, 4, 5인 승객들의 sibsp, alone 컬럼 조회하기.
# df_notalone 데이터에 저장하기
#1
df_notalone = titanic.loc[(titanic.sibsp == 3) | (titanic.sibsp == 4) | (titanic.sibsp == 5)]
df_notalone[["sibsp", "alone"]]
#2
#titanic.loc[행값처리필터, 컬럼값]
df_notalone = titanic.loc[(titanic.sibsp == 3) | (titanic.sibsp == 4) | (titanic.sibsp == 5)], [["sibsp", "alone"]]
df_notalone
#3
df_notalone = titanic.loc[titanic.sibsp.isin([3,4,5]),["sibsp", "alone"]]
df_notalone

# class 컬럼 중 First, Second인 행만 조회하기 df_class12 데이터에 저장
# titanic.class.unique() #class 예약어. 사용 불가
titanic["class"].unique()
titanic.alone.unique()
df_class12 = titanic.loc[titanic["class"].isin(["First", "Second"])]
df_class12["class"].value_counts()

# df : titanic 데이터에서 나이(age), 성별(sex), 클래스(class), 요금(fare),
#       생존여부(suvide) 컬럼만 가진 데이터 프레임 객체
df = titanic[["age", "sex", "class", "fare", "survived"]]
df.info()
# df 레코드 건수
len(df)

# class 컬럼으로 데이터 분할하기
# groupby(컬럼명) : 커럶의 값, 그룹화된 DataFrame
grouped = df.groupby("class")
grouped
# key : 분리된 컬럼값
# group : 분리된 DataFrame 객체
for key, group in grouped :
    print("=== kry", key, end=",")
    print("=== cnt", len(group), type(group))

df["class"].value_counts()
# 그룹별 평균
grouped.mean()
df.groupby("class").mean()

# 3등석 정보만 조회하기
group3 = grouped.get_group("Third")
group3.info()
group3.head()

# class, sex 컬럼으로 분할하기
grouped2 = df.groupby(["class", "sex"])
for key, group in grouped2 :
    print("=== kry", key, end=",")
    print("=== cnt", len(group), type(group))

# 3등석 여성 정보만 조회하기
group3f = grouped2.get_group(("Third", "female"))
group3f
group3f.info()

grouped2.std()
grouped2.mean()

# fare의 평균구하기
grouped2.fare.mean()
grouped2.fare.mean().index
grouped2.fare.mean().values

# 클래스별 가장 많은 나이와, 가장 적은 나이를 출력하기
grouped.age.max()
grouped.age.min()
df.groupby("class").age.max()
df.groupby("class").age.min()

# 클래스명 성별 나이가 가장 많은 나이와, 가장 적은 나이를 출력하기
grouped2.age.max()
grouped2.age.min()
df.groupby(["class", "sex"]).age.max()
df.groupby(["class", "sex"]).age.min()

# agg(함수이름) 함수 : 여러개의 함수를 여러개의 컬럼에 적용할 수 있는 함수. 
#                       사용자 정의함수 적용
def max_min(x) :
    return x.max() - x.min()

# grouped 데이터에 max_min 함수 적용
agg_maxmin = grouped.agg(max_min)
agg_maxmin 

#grouped 데이터에 max 조회
grouped.max() #max 함수 호출
grouped.agg(max) #agg 함수에서 max 함수 호출

#grouped 데이터에 최대, 최소값 조회
grouped.agg(['max','min'])

# 각 컬럼마다 다른 함수 적용할 수 있음.
# 요금(fare): 평균값, 최대값
# 나이(age): 평균값
grouped.agg({'fare':['mean','max'], 'age': 'mean'})
#             fare                  age
#             mean       max       mean
#class                                 
#First   84.154687  512.3292  38.233441
#Second  20.662183   73.5000  29.877630
#Third   13.675550   69.5500  25.140620

#----------------------------------------------------------
# filter(조건) 함수: 그룹화된 데이터의 조건 설정.
# grouped 데이터의 갯수가 200개 이상인 그룹만 조회하기
grouped.count()
#Out[171]: 
#        age  sex  fare  survived
#class                           
#First   186  216   216       216
#Second  173  184   184       184
#Third   355  491   491       491
#----------------------------------------------------------
filter1 = grouped.filter(lambda x : len(x) >= 200)
filter1.info()
filter1['class'].value_counts()
#Out[176]: 
#Third     491
#First     216
#Second      0
#Name: class, dtype: int64

#----------------------------------------------------------
#age 컬럼의 평균이 30보다 작은 그룹만을 filter2에 저장하기
grouped.mean()
filter2 = grouped.filter(lambda x : x.age.mean() < 30)
filter2.groupby('class').mean()
filter2.info()
filter2['class'].value_counts()
#Out[193]: 
#Third     491
#Second    184
#First       0
#Name: class, dtype: int64

#----------------------------------------------------------
# 두개의 DataFrame 연결하기
#stockprice.xlsx, stockvaluation.xlsx 데이터를 읽기
df1 = pd.read_excel('data/stockprice.xlsx')
df2 = pd.read_excel('data/stockvaluation.xlsx')
df1
df2

#df1
#Out[196]: 
#       id stock_name          value   price
#0  128940       한미약품   59385.666667  421000
#1  130960     CJ E&M   58540.666667   98900
#2  138250      엔에스쇼핑   14558.666667   13200
#3  139480        이마트  239230.833333  254500
#4  142280     녹십자엠에스     468.833333   10200
#5  145990        삼양사   82750.000000   82000
#6  185750        종근당   40293.666667  100500
#7  192400      쿠쿠홀딩스  179204.666667  177500
#8  199800         툴젠   -2514.333333  115400
#9  204210     모두투어리츠    3093.333333    3475

#df2
#Out[197]: 
#       id       name           eps     bps        per       pbr
#0  130960     CJ E&M   6301.333333   54068  15.695091  1.829178
#1  136480         하림    274.166667    3551  11.489362  0.887074
#2  138040    메리츠금융지주   2122.333333   14894   6.313806  0.899691
#3  139480        이마트  18268.166667  295780  13.931338  0.860437
#4  145990        삼양사   5741.000000  108090  14.283226  0.758627
#5  161390      한국타이어   5648.500000   51341   7.453306  0.820007
#6  181710  NHN엔터테인먼트   2110.166667   78434  30.755864  0.827447
#7  185750        종근당   3990.333333   40684  25.185866  2.470259
#8  204210     모두투어리츠     85.166667    5335  40.802348  0.651359
#9  207940   삼성바이오로직스   4644.166667   60099  89.790059  6.938551

#concat() : df1, df2 데이터를 열 기준으로 연결하기. 물리적인 연결
result1 = pd.concat([df1,df2],axis=1)
result1
#result1
#Out[199]: 
#       id stock_name          value  ...     bps        per       pbr
#0  128940       한미약품   59385.666667  ...   54068  15.695091  1.829178
#1  130960     CJ E&M   58540.666667  ...    3551  11.489362  0.887074
#2  138250      엔에스쇼핑   14558.666667  ...   14894   6.313806  0.899691
#3  139480        이마트  239230.833333  ...  295780  13.931338  0.860437
#4  142280     녹십자엠에스     468.833333  ...  108090  14.283226  0.758627
#5  145990        삼양사   82750.000000  ...   51341   7.453306  0.820007
#6  185750        종근당   40293.666667  ...   78434  30.755864  0.827447
#7  192400      쿠쿠홀딩스  179204.666667  ...   40684  25.185866  2.470259
#8  199800         툴젠   -2514.333333  ...    5335  40.802348  0.651359
#9  204210     모두투어리츠    3093.333333  ...   60099  89.790059  6.938551
result1.info()
#concat() : df1, df2 데이터를 행 기준으로 연결하기. 물리적인 연결
result1 = pd.concat([df1,df2],axis=0)
result1.info()
result1

# Out[205]: 
#        id stock_name          value  ...       bps        per       pbr
# 0  128940       한미약품   59385.666667  ...       NaN        NaN       NaN
# 1  130960     CJ E&M   58540.666667  ...       NaN        NaN       NaN
# 2  138250      엔에스쇼핑   14558.666667  ...       NaN        NaN       NaN
# 3  139480        이마트  239230.833333  ...       NaN        NaN       NaN
# 4  142280     녹십자엠에스     468.833333  ...       NaN        NaN       NaN
# 5  145990        삼양사   82750.000000  ...       NaN        NaN       NaN
# 6  185750        종근당   40293.666667  ...       NaN        NaN       NaN
# 7  192400      쿠쿠홀딩스  179204.666667  ...       NaN        NaN       NaN
# 8  199800         툴젠   -2514.333333  ...       NaN        NaN       NaN
# 9  204210     모두투어리츠    3093.333333  ...       NaN        NaN       NaN
# 0  130960        NaN            NaN  ...   54068.0  15.695091  1.829178
# 1  136480        NaN            NaN  ...    3551.0  11.489362  0.887074
# 2  138040        NaN            NaN  ...   14894.0   6.313806  0.899691
# 3  139480        NaN            NaN  ...  295780.0  13.931338  0.860437
# 4  145990        NaN            NaN  ...  108090.0  14.283226  0.758627
# 5  161390        NaN            NaN  ...   51341.0   7.453306  0.820007
# 6  181710        NaN            NaN  ...   78434.0  30.755864  0.827447
# 7  185750        NaN            NaN  ...   40684.0  25.185866  2.470259
# 8  204210        NaN            NaN  ...    5335.0  40.802348  0.651359
# 9  207940        NaN            NaN  ...   60099.0  89.790059  6.938551


#merge(): id  컬럼을 기준으로 같은 id의 값을 가진 레코드를 병합.
#           sql의 join 비슷함.
result2 = pd.merge(df1,df2)
result2
result2.info()
# result2
# Out[208]: 
#        id stock_name          value  ...     bps        per       pbr
# 0  130960     CJ E&M   58540.666667  ...   54068  15.695091  1.829178
# 1  139480        이마트  239230.833333  ...  295780  13.931338  0.860437
# 2  145990        삼양사   82750.000000  ...  108090  14.283226  0.758627
# 3  185750        종근당   40293.666667  ...   40684  25.185866  2.470259
# 4  204210     모두투어리츠    3093.333333  ...    5335  40.802348  0.651359
# [5 rows x 9 columns]

#on='id': 연결컬럼, merge 컬럼. 두개의 데이터 프레임 병합시 id 컬럼의 값이 같은 경우 병합 
result2 = pd.merge(df1, df2, on='id')
result2
result2.info()

# how="outer" : 병합되는 두개의 데이터의 연결 컬럼의 값이 같지 않아도 조회가능
#               값이 같지 않은 경우 상대컬럼의 값은 NaN 임
#내용이 같지 않은 df1, df2 데이터 조회.
#sql: full outer join
result3 = pd.merge(df1, df2, on="id", how="outer") 
result3

result3 = pd.merge(df1, df2, on="id", how="left")
result3

result3 = pd.merge(df1, df2, on="id", how="right")
result3

# 다른 컬럼을 이용하여 병합하기
# left_on='stock_name' : 왼쪽 데이터프레임의 연결컬럼 설정
# right_on='name' : 오른쪽 데이터프레임의 연결컬럼 설정
# => df1.stock_name 컬럼과 df2.name 컬럼을 연결하여 병합
# => 연결컬럼이름이 다른 경우 병합
result4 = pd.merge(df1, df2, left_on='stock_name', right_on='name')
result4
result4.info()

# df1.stock_name 컬럼과 df2.name 컬럼을 연결하여 병합
# 내용이 다른 레코드도 조회하기
result5 = pd.merge(df1, df2, left_on='stock_name', right_on='name', how="outer")
result5

# df1.stock_name 컬럼과 df2.name 컬럼을 연결하여 병합
# 내용이 다른 레코드인 경우 df1에 있는 데이터만 값이 달라도 조회하기
result6 = pd.merge(df1, df2, left_on='stock_name', right_on='name', how="left")
result6

# df1.stock_name 컬럼과 df2.name 컬럼을 연결하여 병합
# 내용이 다른 레코드인 경우 df2에 있는 데이터만 값이 달라도 조회하기
result7 = pd.merge(df1, df2, left_on='stock_name', right_on='name', how="right")
result7

##################################################################
#  BeautifulSoup: html, xml, 태그 분석 모듈
from bs4 import BeautifulSoup
import urllib.request as req # 인터넷을 통해서 요청을 할 수 있는 모듈. 인터넷 접속 기능 모듈
url = "https://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp"
res=req.urlopen(url) # 인터넷을 통해 url에 연결.
print(res) # url을 통해서 연결된 응답데이터
# BeautifulSoup : html, xml 마크업언어 분석 객체
# soup : res의 태그를 분석하여 DOM의 root노드 저장
soup=BeautifulSoup(res, 'html.parser')
# title 태그 찾기. 내용을 title 변수 저장
title=soup.find('title').string
# wf 태그 찾기. 내용을 wf에 저장
wf=soup.find('wf').string
print(title)
print(wf)

'''
<!CDATA[○ (강수) 4일(월)은 충청권과 남부지방, 
        제주도에, 5일(화)~7일(목)은 전국에 비가 오겠고, 8일(금)은 중부지방과 전라권에 비가 오겠습니다
        
<!CDATA[ 내용 ]]> : 내용만 출력

<!CDATA[  ]]> : CDATA 섹션. 순수 문자열. XML 용어.
                CDATA 섹션내에서 사용되는 태그들은 태그가 아니고 순수한 문자열
'''
728x90
반응형