본문 바로가기

대외활동/ABC 지역주도형 청년 취업역량강화 ESG 지원산업

[220824 ABC - 7일차] PYTHON 라이브러리

반응형

# 파이썬 머신러닝 판다스 데이터 분석 교재 실습 시작 

1. 데이터 과학자가 판다스를 배우는 이유?

- 데이터과학은 데이터를 연구하는 분야이고, 데이터 자체가 가장 중요한 자원이다.

- 실제로 데이터 분석 업무의 80~90%는 데이터를 수집하고 정리하는 일이 차지한다고 볼 수 있다.

- 데이터과학자가 하는 가장 기초적이고 중요한 일은 데이터를 수집하고 분석이 가능한 형태로 정리하는 것이다.

- 판다스 라이브러리는 데이터를 수집하고 정리하는 데 최적화된 도구라고 할 수 있다.

* 데이터를 신뢰할 정도로 만든다? => 데이터 정리를 잘한다!

2. 판다스 자료구조

- 판다스는 시리즈(Series)와 데이터프레임(DataFrame)이라는 구조화된 데이터 형식을 제공한다.

- 이때, 시리즈는 1차원 배열이고, 데이터프레임은 2차원 배열이라는 점에서 차이가 있다.

- 판다스의 1차적인 목적은 서로 다른 여러 가지 유형의 데이터를 공통의 포맷으로 정리하는 것이다.

* 시리즈(1차원 배열)을 여러개 붙여놓으면 데이터프레임(2차원 배열)이 된다.

 

- 일단 외부에 있는 pandas 라이브러리 설치!

! pip install pandas # Jupyter
pip install pandas # Terminal

[ Series(시리즈) ]

- 인덱스(index) 는 데이터 값(value)과 일대일 대응이 된다.

  => 키(k)와 값(v)이 '{k:v}'의 현태로 짝을 이루는 파이썬 딕셔러니(dictionary)와 비슷한 구조를 갖는다.

- 딕셔너리 → 시리즈 변환 : pandas.Series(딕셔너리)

import pandas as pd

dict_data = {'a':1, 'b':2, 'c':3} # 딕셔너리

sr = pd.Series(dict_data) # 딕셔너리 dict_data 를 series 로 변환하는 명령문
print(sr)
print(type(sr))

: 이 경우, 이차원 배열이 아니라 key와 value로 구성된 1차원 배열이다. 여기서는 key가 index와 동일한데, 여기서는 a,b,c가 key이다.

import pandas as pd

list = ['2022-08-23', 3.14, 'abc', True]

sr = pd.Series(list)

print(sr)
print(type(sr))

 : 이 경우, index 값으로 볼 수 있는 key값이 없기 때문에 0 1 2 3의 index가 나온다.

 

- Series 클래스의 index 속성을 이용하여 인덱스 배열을 따로 선택할 수 있다 

  => 인덱스 배열 : Series객체.index

- 또한 데이터 값 배열만을 따로 선택하는 것도 가능하다

  => 데이터 값 배열 : Series객체.values

import pandas as pd

tuple = ('2022-08-23', 3.14, 'abc', True)

sr = pd.Series(tuple)
print(sr)
print(type(sr))
print(sr[2]) # 2의 인덱스 값을 갖는 abc 문자열이 출력된다.

 

- 원소의 위치를 나타내는 주소 역할을 하는 인덱스를 이용하여 시리즈의 원소를 선택한다.(하나도 가능, 여러개도 가능)

import pandas as pd

list = ['2022-08-23', 3.14, 'abc', True]

sr = pd.Series(list, index=['날짜', '숫자', '문자', '논리값'])

print(sr)
print(type(sr))
print(sr["문자"]) 
print()
print(sr[[0,2]])


print()
print(sr['숫자':'논리값'])

: 위의 코드블럭을 살펴보자. 3번째 코드에서 index에 이름을 지정해주는 것을 볼 수 있다. print(sr[[0.2]])에서 index0과 index2의 데이터 값인 날짜와 문자를 보여주고 있다는 것을 확인할 수 있다. 또한, 코드의 마지막줄에서 index의 이름으로도 슬라이싱이 가능하다는 것을 확인할 수 있다.

 

- index 값이 아니라 index의 이름으로도 인덱싱이나 슬라이싱이 가능하다.

- 슬라이싱은 콜론(:)을 사용해서 표현하지만, 연속적이지 않은 두가지 이상의 데이터값을 꺼낼 경우에는 그냥 해당 index를 콤마(,)로 구분할 수 있다.

 

[ 데이터프레임(DataFrame) ]

- 데이터프레임은 2차원 배열이다.

- 마이크로소프트 엑셀(Excel)과 관계형 데이터베이스(RDBMS) 등 컴퓨터 관련 다양한 분야에서 사용된다.

- 시리즈를 열벡터(vector), 데이터프레임은 2차원 벡터 또는 행렬(matrix)라고 한다.

- 데이터프레임은 행 인덱스(row index), 열 이름(column name  또는 column label)로 구분한다.

 

- 딕셔너리 → 데이터프레임 변환 : pandas.DataFrame(딕셔너리 객체)

import pandas as pd

dict = {"c0": [1,2,3], "c1":[4,5,6], "c2":[7,8,9],"c3":[10,11,12]}

df = pd.DataFrame(dict)

print(df)
print(type(df))
print()

 : 이때 딕셔너리의 키(k)가 열 이름이 되고, 값(v)에 해당하는 각 리스트가 데이터프레임의 열이 된다. 행 인덱스에는 정수형 위치 인덱스가 자동으로 지정된다. 이는 따로 지정해주지 않았기 때문에 그렇다.

 

- 행 인덱스/열 이름 설정 : pandas.DataFrame(2차원 배열, index=행 인덱스 배열, columns=열 이름 배열)

import pandas as pd

df = pd.DataFrame([[15, '남', '덕영중'], [17, '여', '수리중'],
                   [19, '여자', '이미중']], 
                  index = ['준서', '효린', '예은'], 
                  columns=['나이', '성별', '학교']) 

print(df)
print()


print(df.index)

print(df.columns)
print()

 : 위의 코드블럭을 살펴보자. df의 3개의 리스트로 구성된 첫번째 리스트는 각 행열의 요소라고 할 수 있다. → 이때 2차원 배열이기 때문에 [] 로 한번 더 감싸야 한다. df의 두번째 index=['준서', '효린', '예은']는 각각의 행 인덱스의 이름을 지정해주는 코드이다. df의 세번째 columns=['나이', '성별', '학교']는 각각의 열 이름을 지정해주는 코드이다. print(df.index) 는 이 데이터의 index를 출력해준다. print(df.colunms)는 이 데이터의 columns를 출력해준다.

 

- csv 파일을 가져와서 가장 먼저 해야할 것은 바로 columns를 확인하는 것이다.

- columns를 변수로 가져와서 사용할 수 있다. 

- df.index는 df.columns보다 사용빈도는 낮을 수도 있지만 알아두는 것이 좋다.

 

- 행 인덱스 변경 : DataFrame 객체.index = 새로운 행 인덱스 배열

- 열 이름 변경 : DataFrame 객체.columns = 새로운 열 이름 배열

import pandas as pd

df = pd.DataFrame([[15, '남', '덕영중'], [17, '여', '수리중'],
                   [19, '여자', '이미중']], 
                  index = ['준서', '효린', '예은'], 
                  columns=['나이', '성별', '학교']) 
                  
df.index=['학생1', '학생2', '학생3']
df.columns=['연령', '남녀', '소속']

print(df)
print()

 : 위의 코드 블럭을 살펴보자. 해당 행 인덱스의 이름과 열 이름을 변경하고자 할 때 사용한다. 단, 변경보다도 지정하고 싶을 경우 많이 사용한다. 변수를 사용할 때 내가 편한 이름으로 변경해서 사용할 때 사용한다.

 

- 다른 방법도 있다.

- 행 인덱스 변경 : DataFrame 객체.rename(index={기존 인덱스:새 인덱스 ... })

- 열 이름 변경 : DataFrame 객체.rename(columns={기존 이름:새 이름 ... })

import pandas as pd

df = pd.DataFrame([[15, '남', '덕영중'], [17, '여', '수리중'],
                   [19, '여자', '이미중']], 
                  index = ['준서', '효린', '예은'], # index 즉, 행 인덱스의 이름을 지정해준다.
                  columns=['나이', '성별', '학교']) # 열 이름을 지정해준다.

df.rename(columns={'연령':'나이', '남녀':'성별', '소속':'중학교명'}, inplace=True)
print(df)
print()

df.rename(index={'학생1':'재석', '학생2':'석진', '학생3':'종국'}, inplace=True)
print(df)

 : 위의 코드 블럭을 살펴보자. 이것도 열 이름을 변경하는 코드이다. 열 이름을 한번에 바꾸고자 할 경우 사용한다. inplace = True는 원본 객체를 직접 변경하기 위해서 사용 False를 사용하면 새로운 객체가 생성되고 원본 객체는 변경되지 않는다. inplace를 설정해주지 않으면 안바뀐다!

 

- 행 삭제 : DataFrame 객체.drop(행 인덱스 또는 배열, axis=0)

- 열 삭제 : DataFrame 객체.drop(열 이름 또는 배열, axis=1)

import pandas as pd

exam_data = {
    '수학':[90,80,70], '영어':[98,89,95],
    '음악':[85,95,100], '체육':[100,90,90]
}

# 원본 데이터
df = pd.DataFrame(exam_data, index=['서준', '우현', '인아'])
print(df)
print()

# df2에 df 데이터 모두 복제
df2 = df[:]
print(id(df), id(df2)) # 깊은복사인지 얕은복사인지 확인한다. 주소값이 달라? ==> 얕은 복사 df2는 100퍼센트 사본데이터이다!
print()

df2.drop('우현', inplace=True) # '우현'의 데이터가 모두 날아감
print(df2)
print()

df3 = df[:]
df3.drop(['서준','인아'], axis=0, inplace=True) # '서준'과 '인아'데이터 삭제 axis=0은 행 기준으로 삭제함 1은 열 기준으로 삭제함
print(df3)
print()

df4 = df[:]
df4.drop(['서준','인아'], inplace=True)
print(df4)
print()

df4.drop(['음악'], axis=1, inplace=True) # '음악' 열을 삭제
print(df4)

 : 행을 삭제할 때는 축(axis) 옵션으로 axis=0을 입력하거나, 별도로 입력하지 않는다. 축 옵션으로 axis=1을 입력하면 열을 삭제한다. 또한, 원본 객체를 직접 변경하기 위해서는 inplace = True 옵션을 추가한다. inplace=False 옵션으로 추가하게 되면 새로운 객체가 생성되고, 원 객체는 변경없이 그대로 유지된다.

 

- 행 선택

구분 loc iloc
탐색 대상 인덱스 이름(index label) 정수형 위치 인덱스(integer position)
범위 지정 가능(범위의 끝 포함)
예) [ 'a' : 'c' ] → 'a', 'b', 'c'
가능(범위의 끝 제외)
예 [ 3 : 7 ] → 3, 4, 5, 6 (*7제외)
import pandas as pd

exam_data = {
    '수학':[90,80,70, 80], '영어':[98,89,95,75],
    '음악':[85,95,100,90], '체육':[100,90,90,55]
}

df = pd.DataFrame(exam_data, index=['재석', '석진', '우영', '지효'])
print(df)
print()

ld1 = df.loc['석진']
print(ld)
print()

ld2 = df.iloc[1]
print(ld)
print()

ld3 = df.loc[['석진', '우영']]
print(ld3)
print()

ld4 = df.iloc[[1,2]]
print(ld4)
print()

ld5 = df.loc['재석':'석진']
print(ld5)
print()

ld6 = df.iloc[1:3]
print(ld6)

 : 1) loc와 iloc의 차이 → loc : 행 인덱스 이름을 기준, iloc : 행 인덱스 위치 번호를 기준

   2) 연속적이지 않은 2개 이상의 데이터를 가져오려면 [] 두개로 [[]] 이렇게 감싸줘야 한다.

   3) 비연속적인 데이터를 2개 이상 가져오는 것은 콤마(,)로 구분한다.

   4) 연속적인 데이터는 [] 하나로 감싼다.

   5) 연속적인 데이터의 범위를 설정하는 것은 콜론(:)으로 구분한다.

 

- 열 1개 선택(시리즈 생성) : DataFrame 객체["열 이름"] 또는 DataFrame 객체.열 이름

import pandas as pd

# 열을 선택해보자
zz = df['음악']
print(zz)
print()

z1 = df.음악
print(z1)
print()

 : 가져온 빅데이터에서 열 이름이 띄어쓰기가 되어 있는 경우에는 []로 묶어서 가져온다. df.주소 시군구 ==> 띄어쓰기가 있어서 불가능하다. df.['주소 시군구'] 이렇게 설정해야 한다.

 

- 열 n개 선택(데이터프레임 생성) : DataFrame 객체[ [ 열1, 열2, ... , 열n ] ]

import pandas as pd

# 열을 선택해보자
zz = df['음악']

# 두개 이상의 데이터를 가져오는 것도 가능하다.
z2 = df[['음악', '체육']]
print(z2)

- 데이터프레임 슬라이싱 : DataFrame 객체.[ 시작 인덱스 : 끝 인덱스 : 슬라이싱 간격 ]

- 시작 인덱스는 포함하고 끝 인덱스는 포함하지 않는다.

- 시작 인덱스와 끝 인덱스에 아무것도 안넣으면 각각 맨 처음부터, 맨 마지막까지 포함하는 것이다.

import pandas as pd

zz = df['음악']
print(zz)
print()

z5 = df[::2]
print(z5)
print()

 

- 역순 인덱싱(정렬)

import pandas as pd

zz = df['음악']
print(zz)
print()

z6 = df[::-1]
print(z6)
print()

 

예제) 인덱스 2부터 인덱스 3까지 2의 간격으로 가져온다.

import pandas as pd

zz = df['음악']
print(zz)
print()

z7 = df[2:3:2]
print(z7)
print()

 

- 원소 선택하기 ( 특정 열과 특정 행의 데이터만 갖고 오기 )

1) 인덱스 이름 : DataFrame 객체.loc[행 인덱스, 열 이름]

2) 정수 위치 인덱스 : DataFrame 객체.iloc[행 번호, 열 번호]

import pandas as pd

zz = df['음악']
print(zz)
print()

# 인덱스의 이름으로 가져오자
z8 = df.loc[['재석', '석진'], ['음악', '수학']]
print(z8)
print()

# 행 인덱스 위치 번호로 가져오자
z9 = df.iloc[0:3,2:]
print(z9)

 

- 한개의 행에 특정 열을 가져올 수 있다 iloc 역시 사용 가능하다.

import pandas as pd

zz = df['음악']
print(zz)
print()

z10 = df.loc['지효',['음악','수학']]
print(z10)

 

- 열 추가 : DataFrame 객체[ '추가하려는 열 이름' ] = 데이터값

- 값을 하나만 지정해주면 해당 값을 기본값으로 해 데이터를 입력해준다.

import pandas as pd

zz = df['음악']
print(zz)
print()

df['화학']=80
print(df)
print()

# abc라는 열을 추가해서 화학과 국어의 데이터를 더해줄거야?
# df[abc] = df['화학']+df['국어']

df['abc'] = df['수학'] + df['화학']
print(df)

 

- 행 추가 : DataFrame.loc[ '새로운 행 이름' ] = 데이터 값(또는 배열)

import pandas as pd

zz = df['음악']
print(zz)
print()

df.loc['영어'] = 0
print(df)

 

- 원소 값 변경 : DataFrame 객체의 일부분 또는 원소를 선택 = 새로운 값

import pandas as pd

zz = df['음악']
print(zz)
print()

df.rename(index={'영어':'나나'}, inplace=True)
print(df)

df.loc['나나'] = [20, 30, 40, 50, 60, 70]
print(df)

df.drop('석진', inplace=True)

 : 위의 코드 블럭을 살펴보자. 우선 df.rename의 코드로 인덱스 영어를 나나로 변경해준다. 그리고 난 뒤 loc의 코드로 인덱스 나나행에 리스트 데이터 값을 넣어준다. 그 후 석진의 데이터를 삭제한다.

 

- 특정 행과 특정 열에 해당되는 특정 데이터 하나를 변경하고자 할 경우

import pandas as pd

zz = df['음악']
print(zz)
print()

df.loc['재석']['체육'] =30
print(df)

df.iloc[0][3] = 20
print(df)

df.loc['재석', '체육'] = 50
print(df)

 

- 한가지 행에 두가지 열을 변경하자

import pandas as pd

zz = df['음악']
print(zz)
print()

df.loc['재석', ['음악', '체육']] = 100, 80
print(df)

df.drop('나나', inplace=True)
print(df)

 

- 행과 열의 위치 바꾸기(행 열 전환) : DataFrame 객체.transpose() 또는 DataFrame 객체.T

import pandas as pd

zz = df['음악']
print(zz)
print()

abc = df.transpose()
print(abc)

 

[ 인덱스 활용 ] 

- 특정 열을 행 인덱스로 설정 : DataFrame 객체.set_index( [ '열 이름' ] 또는 '열 이름')

- set_index() 메소드를 사용하여 행 인덱스를 새로 지정하면 기존 행 인덱스는 삭제된다.

exam_data = {
    '이름' : ['재석', '지효', '석진', '우영'],
    '수학':[90,80,70, 80], '영어':[98,89,95,75],
    '음악':[85,95,100,90], '체육':[100,90,90,55]
}

dfx = pd.DataFrame(exam_data)
print(dfx)

dfx.set_index('이름', inplace=True)
print(dfx)

 : 위의 코드 블럭을 살펴보자. 여기서는 해당 인덱스는 0 1 2 3 이다.

1) 그런데 이름에 해당하는 열을 인덱스로 사용하고 싶다면? df.set_index('해당열이름', inplace=True/False)는 어떤 열 자체로 index를 설정한다. 

2) index를 왜 지정할까? => 데이터를 찾을 때 index를 기준으로 찾아야 한다. 즉, 주소값을 지정해준다는 소리

 

- 여러개의 열을 행 인덱스로 설정하는 것도 가능하다.

exam_data = {
    '이름' : ['재석', '지효', '석진', '우영'],
    '수학':[90,80,70, 80], '영어':[98,89,95,75],
    '음악':[85,95,100,90], '체육':[100,90,90,55]
}

dfx = pd.DataFrame(exam_data)
print(dfx)

dfx.set_index(['이름', '음악'],inplace=True)
print(dfx)

 

- 행 인덱스 재배열 

- reindex() 메소드를 사용하면 데이터프레임의 행 인덱스를 새로운 배열로 재지정할 수 있다.

- 새로운 배열로 행 인덱스를 재지정 : DataFrame 객체.reindex( 새로운 인덱스 배열 )

exam_data = {
    '이름' : ['재석', '지효', '석진', '우영'],
    '수학':[90,80,70, 80], '영어':[98,89,95,75],
    '음악':[85,95,100,90], '체육':[100,90,90,55]
}

dfx = pd.DataFrame(exam_data)
print(dfx)
print()

dfx.set_index(['이름'],inplace=True)

new_index=['재석', '지효', '석진', '우영', '호민', '길동']
rdfx = dfx.reindex(new_index, fill_value=0)

print(rdfx)

 : 위의 코드블럭을 살펴보자. fill_value = 0은 비어있는 데이터 값들을 모두 0으로 집어넣어준다. 유의할 점은 데이터가 4,5,NaN,7,8로 되어 있는 것과 4,5,0,7,8로 되어 있는 것은 많은 차이가 난다. 평균값을 구하는 등의 값들이 수치가 많이 달라질 수 있다.

※ 평균을 많이 사용하는 이유는? => 해석하기 좋기 때문(기준점으로)

결측치가 생겨도 데이터 수가 많으면 평균치 같은 수치가 흔들리지 않는다.

NaN으로 그냥 해석하냐 vs 0으로 넣어서 해석하냐의 차이점은 그냥 그때그때 합리적 의사결정을 통해서 결정을 내리는 것이다.

 

- 인덱스 초기화(없애기)

- 정수형 위치 인덱스로 초기화 : DataFrame 객체.reset_index()

exam_data = {
    '이름' : ['재석', '지효', '석진', '우영'],
    '수학':[90,80,70, 80], '영어':[98,89,95,75],
    '음악':[85,95,100,90], '체육':[100,90,90,55]
}

dfx = pd.DataFrame(exam_data)
print(dfx)
print()

dfx.set_index(['이름'],inplace=True)
print(dfx)

dfx = dfx.reset_index()
print(dfx)

 

- 행 인덱스를 기준으로 데이터프레임 정렬

- 행 인덱스 기준 정렬 : DataFrame 객체.sort_index()

exam_data = {
    '이름' : ['재석', '지효', '석진', '우영'],
    '수학':[90,80,70, 80], '영어':[98,89,95,75],
    '음악':[85,95,100,90], '체육':[100,90,90,55]
}

dfx = pd.DataFrame(exam_data)
print(dfx)
print()

dfx.set_index(['이름'],inplace=True)
print(dfx)

# 가나다 순으로 정렬
dfx = dfx.sort_index(ascending=False)
print(dfx)

 : ascending = False 옵션을 사용하여 내림차순으로 정렬한다. 

   ascending = True 옵션을 사용해서 오름차순으로 정렬한다.

 

- 특정 열의 데이터를 기준으로 데이터프레임을 정렬할 수 있다.

- 열 기준 정렬 : DataFrame 객체.sort_values()

exam_data = {
    '이름' : ['재석', '지효', '석진', '우영'],
    '수학':[90,80,70, 80], '영어':[98,89,95,75],
    '음악':[85,95,100,90], '체육':[100,90,90,55]
}

dfx = pd.DataFrame(exam_data)
print(dfx)
print()

dfx = dfx.sort_values(by='음악', ascending=False)
print(dfx)

 

LIST