데이터분석(AI학습 21)

이유진·2024년 7월 8일

--03.DataFrame.ipynb--

DataFrame

  • Series가 1차원이라면 DataFrame은 2차원으로 확대된 버젼
  • Excel spreadsheet이라고 생각하면 이해하기 쉬움
  • 2차원이기 때문에 인덱스가 row, column로 구성됨
  • row는 각 개별 데이터를, column은 개별 속성을 의미
  • Data Analysis, Machine Learning에서 data 변형을 위해 가장 많이 사용

import numpy as np
import pandas as pd
import os

base_path ='/content/drive/MyDrive/dataset'
filepath = os.path.join(base_path, 'titanic.csv')

df = None
def load_titanic() :
return pd.read_csv(filepath) # CSV 파일로 부터 -> DataFrame 으로 읽어오기.

df = load_titanic()
df

컬럼 의미

survival - Survival (0 = No; 1 = Yes) 생존여부

class - Passenger Class (1 = 1st; 2 = 2nd; 3 = 3rd) 방 등급

name - Name 탑승객 명

sex - Sex 성별

age - Age 나이

sibsp - Number of Siblings/Spouses Aboard 합승객

parch - Number of Parents/Children Aboard 부모, 자녀

ticket - Ticket Number 탑승 티켓 번호

fare - Passenger Fare 티켓 가격

cabin - Cabin 객실 명

embarked - Port of Embarkation (C = Cherbourg; Q = Queenstown; S = Southampton) 선박(최종 목적지)

boat - Lifeboat (if survived)

body - Body number (if did not survive and body was recovered)

df.head()

df.tail()

DataFrame 데이터 파악하기

  • shape 속성 (row, column)
  • describe 함수 - 숫자형 데이터의 통계치 계산 (descriptive statistics : 기술통계량)
  • info 함수 - 데이터 타입, 각 아이템의 개수 등 출력

df.shape

숫자형 데이터에 대해 기술통계량 정보

count, mean, std : 개수, 평균, 표준편차

max, min : 최대, 최소

25%, 50%, 75% : 하위 4분위 하위 2분위...

df.describe()

df.info()

df

인덱스(index)

  • index 속성
  • 각 아이템을 특정할 수 있는 고유의 값을 저장
  • 복잡한 데이터의 경우, 멀티 인덱스로 표현 가능

df.index

df.index.values

컬럼(column)

  • columns 속성
  • 각각의 특성(feature)을 나타냄
  • 복잡한 데이터의 경우, 멀티 컬럼으로 표현 가능

df.columns

.values

df.values # row 데이터들의 2차원 array

DataFrame 생성하기

array-like 객체 사용하여 생성 (list, tuple, array, ....)

df = pd.DataFrame([1, 2, 3])

3 row x 1 col의 DataFrame 생성

df

pd.DataFrame([
'dog',
'cat',
'bird',
])

df = pd.DataFrame([
[1, 2, 3],
[4, 5, 6],
])
df

shape, ndim, size, len()

len(df)

df.shape

df.ndim

df.size

df.dtype ---> dtype은 DataFrame이 아닌 각 column마다 있다.

column, index 변경

df2 = pd.DataFrame([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
], columns=['a', 'b', 'c', 'd'])

df2

df2.columns

컬럼 변경

df2.columns = ['국어', '영어', '수학', '과학']
df2

df2.index

index 변경

df2.index = ['이유진', '이지민', '용호중']
df2

dict로 DataFrame 만들기

data = {'a':100, 'b':200, 'c':300}

pd.DataFrame(data, index = ['x'])

pd.DataFrame(data, index = ['x', 'y', 'z'])

data = {'a':[100], 'b':[200], 'c':[300]}
pd.DataFrame(data)

data = {'a' : [100, 200, 300], 'b': ['cat', 'dog', 'bird'], 'c': [40, 50, 60]}
pd.DataFrame(data)

Series로 부터 DataFrame 생성하기

  • 각 Series의 인덱스 → column
    -결국 DataFrame = Series x Series x ... (Series 가 쌓인 형태가 DataFrame)

a = pd.Series([100, 200, 300], ['a', 'b', 'c'])
b = pd.Series([101, 202, 303], ['a', 'b', 'c'])
c = pd.Series([110, 220, 330], ['a', 'b', 'c'])

pd.DataFrame([a,b,c])

df2

특정 컬럼 이름 변경 : rename()

df2.rename(columns={'국어':'kor', '과학':'Sci'})

set_index(), reset_index()

df2.reset_index()

df2.reset_index(drop=True)

df2.set_index('국어')

df2

퀴즈 : df2의 index와 '국어'컬럼을 바꾸려면?

df2.reset_index().set_index('국어')

inplace = True

대부분의 함수들이 원본변화 안시킨다. 원본 변화 발생시키려면 inplace=True

df3 = df2
df3

df3.set_index('국어',inplace=True)

df3

Multi-level index, Multi-level column

pd.DataFrame({'k':[10]})

pd.DataFrame({('k0', 'k1') : [10]})

pd.DataFrame({('k0', 'k1') : {'a':10, 'b':40}})

pd.DataFrame({('k0', 'k1') : {('a1', 'a2'):10, ('b1', 'b2'):40}})

pd.DataFrame({
('k', 'k1') : [10, 20, 30, 31],
('k', 'k2') : [40, 50, 60, 61],
('j', 'j1') : [70, 80, 90, 91],
('j', 'j2') : [100, 110, 120, 121],
}, index = [['서울','서울', '경기', '경기'], ['평일','휴일', '평일', '휴일']])

column 선택하기

  • 기본적으로 [ ]는 column을 추출
  • 컬럼 인덱스일 경우 인덱스의 리스트 사용 가능
    • 리스트를 전달할 경우 결과는 Dataframe
    • 하나의 컬럼명을 전달할 경우 결과는 Series

df = load_titanic()

df.head()

하나의 컬럼 선택

df[0] # 에러? index 0을 선택하는게 아니다!!! [안에는 특정 컬럼명이 들어가야한다!!!!]

df['Survived'] # 결과값은 Series

df.Survived # 결과값은 Series

df.Age

복수 컬럼 선택하기

df[['Survived']] # numpy때도 배웠지만 [안에 []를 넣게 되면 차원축소 안됨.]

df[['Survived', 'Age', 'Name']]

DataFrame에서 row선택

DataFrame slicing

  • dataframe의 경우 기본적으로 [ ] 연산은 column 선택에 사용 (인덱싱 용이 아니다!!)
  • 하지만, slicing은 row 레벨로 지원

df.head(15)

df[:10]

df[7:10]

df[::10] # 10개씩 건너 뛴다.

row 선택하기

  • Series의 경우 [ ]로 row 선택이 가능하나, DataFrame의 경우는 기본적으로 column을 선택하도록 설계
  • .loc[ ], .iloc[ ]로 row 선택 가능
    • loc - 인덱스 자체를 사용
    • iloc - 0 based index로 사용
    • 위 두가지는 ,를 사용하여 column 선택도 가능

df.head()

index변경

df.index = np.arange(len(df)) + 100
df

loc[index]

df.loc[986]

df.loc[[986]]

df.loc[[986, 100, 110, 990]]

df.loc[np.arange(100, 105)]

df[100:105] # 첫번째 행을 0이라고 생각했을 때 기준으로 실행하여 위 코드와 결과 다름!!!

iloc[0-base index]

df.iloc[0] # 0번째 row

df.iloc[[0]]

df.iloc[[0, 100, 200, 2]]

df.head(3)
df[:3]
df.iloc[:3] # 3개 전부 동일한 결과 나옴.

row, column 동시에 선택하기

loc[ ], iloc[ ] 속성을 이용할때, 콤마를 이용하여 둘다 명시 가능

df.tail()

df.loc[986, 'Survived']

df.iloc[-5, 1] # column도 0-base로 접근 가능!! 중요!!!

df.head()

df.iloc[2, 3]

df.loc[102, 'Name']

df.loc[[986, 100, 110, 990],['Survived', 'Age', 'Name', 'Sex']]

df.loc[[986, 100, 110, 990]][['Survived', 'Age', 'Name', 'Sex']]

df[['Survived', 'Age', 'Name', 'Sex']].loc[[986, 100, 110, 990]]

df.loc[102, 'Name']

df['Name'][102]

df.iloc[2]['Name']

boolean selection으로 row 선택하기

  • numpy에서와 동일한 방식으로 해당 조건에 맞는 row만 선택

나이가 30대 이면서 1등 객실에 탑승한 승객들 선택

df.Pclass # 등급이 1,2,3

df.Pclass.unique()

df.Pclass.value_counts()

pclass_mask = df['Pclass'] == 1 # Boolean Mask
pclass_mask

df[pclass_mask] # 1등객실에 탄 탑승객만 나옴.

30대에 대한 boolean mask

age_mask = (df.Age >= 30) & (df.Age < 40)
age_mask

df[age_mask]

df[age_mask & pclass_mask]

df[age_mask & pclass_mask]['Pclass', 'Age']]

도전] 3등급 객실의 생존자 수는?

결과 119명

sur_mask = df['Survived'] == 1
pclass_mask2 = df['Pclass'] == 3
len(df[pclass_mask2 & sur_mask])

강사님 코딩

((df['Pclass'] == 3) & (df['Survived'] ==1)).sum()

도전] 남성과 여성의 생존률은 ?

[결과 예시]

남성생존률 0.18890814558058924

여성생존률 0.7420382165605095

sex_mask = df['Sex'] == "male"
sex_mask2 = df['Sex'] == 'female'
df[sex_mask]['Survived'].mean()

df[sex_mask2]['Survived'].mean()

새 column 추가하기

  • [ ]를 사용하여 추가하기
  • insert 함수 사용하여 원하는 위치에 추가하기

df['Age_Double'] # 없는컬럼 검색시 오류

df['Age'] * 2

df['Age_Double'] = df['Age'] * 2 # 새로운 컬럼 추가

df

df['Age_triple'] = df.Age_Double + df.Age

df[['Age', 'Age_Double', 'Age_triple']]

df.insert(3,'Fare10', df['Fare'] / 10)

df.head()

column 삭제하기

  • drop 함수 사용하여 삭제
    • axis= 사용
    • 리스트를 사용하여 멀티플 삭제 가능

df.drop('Age_triple') # KeyError: "['Age_triple'] not found in axis" 행은 0번째 axis 열은 1번째 axis

df.drop('Age_triple', axis=1).head()

df.head()

행 (row) 추가 삭제

  • 추가 loc[]
  • 삭제 drop()

df2 = pd.DataFrame([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
], columns=['a','b','c','d'], index = ['김길동', '최진수', '허수민'])
df2

df2.loc['김갑수'] = pd.Series([100, 200, 300, 400], index=['a', 'b', 'c', 'd'])
df2

df2.loc['김철수'] = [101,202,303,404]
df2

df2.drop(['최진수', '허수민']) # axis = 0

행 삽입

df2.insert <---- column 삽입 용도!!!!!!!! 구분 잘해야함!!!!!!!!!!!

df2

df2.iloc[:2]

df2.iloc[2:]

pd.concat([
df2.iloc[:2],
pd.DataFrame({'a':10, 'b':20, 'c':30, 'd':40}, index=['정태경']),
df2.iloc[2:],
])

방법2

df_t = df2.T
df_t.insert(2,'정태경', [10, 20, 30, 40])
df_t.T

Correlation : 상관관계

컬럼, 속성, 변수, 특징(feature) <- 다 똑같은 말

상관관계 (correlation)

특정한 '값' 과의 상관관계, 두 변수간의 흐름이 얼마나 비슷한 가를 나타내는 척도

가령

a 값이 증가할때 b 값도 증가하나?(혹은 감소하나?).

증감의 성향은 어떠한가? <-- 데이터의 분포, 추세를 보는데 중요

-1 ~ +1 사이로 표현한다

+1 에 가까울수록 a, b 둘이 비슷하게 증감 한다.

-1 에 가까울수록 a, b 증감은 반대로 진행 한다.

절대값이 클수록 관계가 크다 라고 볼수 있다.

그러나

+1 에 가깝다고 해서 이것이 '인과관계' 를 나타내는 것은 아니다. (일수도, 아닐수도 있는것.)

변수(column) 사이의 상관계수(correlation)

  • corr함수를 통해 상관계수 연산 (-1, 1 사이의 결과)
    • 연속성(숫자형)데이터에 대해서만 연산
    • 인과관계를 의미하진 않음

df = load_titanic()
df.head()

df.corr()

절대값이 높게 나온다.==> 상관관계가 높다는 뜻이다.

Survived -Pclass 간의 관계

Pclass-Fare 와의 관계

Fare-Survived 와의 관계

parch-fare : parent-children

Age - sibsp :

parch - sibsp : 가족 승선 여부.

import matplotlib.pyplot as plt

plt.matshow(df.corr())

수치(numerical) & 범주형(categorical) 데이터의 이해

  • dtype 에 대한 이야기가 아니다!

df.info()

수치형(Numerical Type) 데이터

  • 연속성을 띄는 숫자로 이루어진 데이터
    • 예) Age, Fare 등
  • 대소비교, 산술연산 (평균, 합계, 중간값) 의미있슴

범주형(Categorical Type) 데이터

  • 연속적이지 않은 값(대부분의 경우 숫자를 제외한 나머지 값)을 갖는 데이터를 의미
    • 예) Name, Sex, Ticket, Cabin, Embarked
  • 어떤 경우, 데이터타입(dtype)이 숫자형 타입이라 할지라도 개념적으로 범주형으로 처리해야할 경우가 있음
    • 예) Pclass, Survived
  • 대소비교, 산술연산(평균, 합계, 중간값...) 의미없슴

Age 를 생각해보자

대소비교 가능한가 : OK

평균값 (연산) 의미 있나 : ex) 탑승객의 평균 나이. OK

나이 40 = 나이 20 x 2

연속성을 띄고 있나? : OK

따라서 '수치형' 으로 다루면 된다.

Pclass 은 어떠한가 . 일단 이것도 dtype int64 타입이긴 한데. 그러면 수치형인가?

이들의 대소 관계 비교가 의미 있나? 1등급 < 3등급 ???

평균값은 의미 있나? 승객들의 객실등급 평균은 2.308641975308642 입니다.

3등급 객실 = 1등급 객실 x 3 ???? <-- 의미 없는 연산

이는 단순히 1등급, 2등급, 3등급 이란 '범주(category)' 를 나타내는 (구별하기 위한) 데이터 이다.

Pclass 타입 변환하기

df.info()

df['Pclass'] = df['Pclass'].astype(str)

df.info()

Age 변수 변환하기

  • apply(func) 함수 활용

10대, 20대, 30대

Age 값이 너무 세분화(?) 되어 있어 보인다.?

그래서 이렇게 해볼수 있습니다.

10대, 20대, 30대.. <-- 이런식으로 '분류(Categorical)' 하는 식으로 변환해볼수도 있을겁니다.

22.0 23.0 ,.. ==> '20대' 로 분류

30.0 31.0 32.0 ... ==> '30대' 로 분류

위와 같이 '숫자형' 데이터를 다시 '분류형Category' 으로 만들어 보고 싶을수도 있습니다.

def age_categorize(age) :
if np.isnan(age) :
return -1
return int(age/10) * 10

age_categorize(34)

age_span = df['Age'].apply(age_categorize)
age_span

age_span.unique()

df['Age_span'] = age_span

df

One-hot encoding

범주형(categorical) 데이터 전처리

범주형 데이터는 연산이 불가하다 (혹은 의미 없다)

그래서 '처리' 가 가능하도록 데이터를 바꿔주어야 할 필요가 있다. (????)

범주형 테이터 컬럼한개를 '범주의 개수' 만큼 늘려서

범주에 해당하는 값에만 '1' 을 주고

나머지에는 '0' 을 주어 처리하는 방법

--> 이를 One-hot encoding 이라 합니다 <-- 결국 '1' 은 한개만 등장한다

가령

Color 란 컬럼에 "Red", "Green", "Blue" 라는 '세가지' 종류의 범주값만 있다면

'세 개'의 '0, 1 로 구성된 값'으로 변환시키는 겁니다 ('1' 은 한개만 등장)

Red -> [1, 0, 0]

Green -> [0, 1, 0]

Blue -> [0, 0, 1]

df['Pclass'].unique()

  • 범주형 데이터는 분석단계에서 계산이 어렵기 때문에 숫자형으로 변경이 필요함
  • 범주형 데이터의 각 범주(category)를 column레벨로 변경
  • 해당 범주에 해당하면 1, 아니면 0으로 채우는 인코딩 기법
  • pandas.get_dummies 함수 사용
    • drop_first : 첫번째 카테고리 값은 사용하지 않음

pd.get_dummies(df)

pd.get_dummies(df, columns = ['Pclass', 'Sex'])

pd.get_dummies(df, columns = ['Pclass', 'Sex'], drop_first=True)

굳이 one-hot encoding 의 첫번째 column 은 필요없다. (없어도 '분류' 가능)

Pclass_1, Sex_Female, Embarked_C 이 없다!

왜 이런게 필요할까? (이유는 아래참조)

이렇게 one-hot encoding 함으로,

범주형 데이터도 '연산'이 가능한 형태로 바뀌었다.

도대체 '연산'이 무엇이기에?

머신러닝등을 하다보면 범주형 데이터에 대한 예측값이 정확히 [1, 0, 0] 이런식으로 나오지 않는다

수~~~ 많은 '연산'을 통해 [0.98232424, 0.0003455, 0.000455] <-- 이런식으로 나온다.

[0.98232424, 0.0003455, 0.000455] 은 다음 세개중 무엇에 가장 가까운가?

[1, 0, 0]

[0, 1, 0]

[0, 0, 1]

이 판단 과정도 산술 '연산'인 것이다.

이것이 바로 '연산' 이 가능한 형태라는 의미다.

NaN 처리하기

NaN : Not a Number <-- '결측값(missing value)' 이라고도 한다

(NA: Not Available 라고도 표기하며 np.NaN, None 등이 해당한다)

여러가지 이유에 의해 데이터 안에 결측값이 존재할수 있다. (누락, 소실 등...)

값이 0 이라는 뜻이 아니다! 값이 '없다'는 뜻이다.

데이터 분석 이전에 반드시 NaN 값 존재 여부도 확인해야 한다

데이터 분석시 '전처리' 단계에서 NaN 값에 대한 적절한 전처리를 해주어야.

정확한 분석과 인공지능 학습을 기대할 수 있다.

일반적으로 두가지 방법 사용하여 NaN 처리

1. 결측값은 무시하든지

2. 다른값으로 채우든지

NaN 값 확인

  • describe, info함수를 통하여 개수 확인
  • isna함수를 통해 boolean 타입으로 확인

df.info()

df.isna()

df['Age'].isna()

df[df['Age'].isna()] # 결측된 데이터만 뽑기

NaN 처리 방법

  • 데이터에서 삭제
    • dropna 함수
  • 다른 값으로 치환
    • fillna 함수

df.dropna()

183개의 row 가 나온다 (891개에서 확 줄었다)

index 나 PassengerId 를 보면 중간중간에 빠진거 확인

기본적으로 row 기준(axis=0)으로 동작한다

각 row 에서, 하나라도 NaN 값이 있으면 해당 row 를 지워버린다

특정 컬럼에 대해서만 dropna 하기

df.dropna(subset=['Age']) # Age 컬럼이 NaN인 경우만 해당 row가 drop됨

'컬럼'에 NaN이 있으면 '컬럼'을 삭제하기

df.dropna(axis=1)

NaN 값 대체하기

  • fillna()
  • ex)평균으로 대체하기
  • ex) 생존자/사망자 별 평균으로 대체하기

df['Age'].mean() # 집계함수는 NaN값을 배제하고 연산!!

df['Age'].fillna(df['Age'].mean())

groupby

  • 데이터를 특정 컬럼으로 묶어서 그룹화
  • 아래의 세 단계를 적용하여 데이터를 그룹화(groupping) (SQL의 group by 와 개념적으로는 동일, 사용법은 유사)
    • 데이터 분할 (split)
    • operation 적용 (apply)
    • 데이터 병합 (combine)

df = load_titanic()
df.head()

groupby(), groups 속성

  • 각 그룹과 그룹에 속한 index를 dict 형태로 표현

groupby(컬럼 혹은 컬럼의 리스트) 괄호 안에 값이 들어갈 수 있다.

grouped_pclass = df.groupby('Pclass')
grouped_pclass # DataFrameGroupBy 객체

groupby 객체의 속성

- groups -> dict 형태

- key -> 컬럼의 각 값

- value -> 인덱스 값들의 list

grouped_pclass.groups # [인덱스 값] 임.

grouped_pclass.size() # 그룹별 데이터의 개수

grouped_gender = df.groupby('Sex')
grouped_gender

grouped_gender.groups

grouped_gender.size()

get_group()

그룹별 DataFrame 리턴

grouped_pclass.get_group(1) # Pclass가 1인 그룹의 DataFrame 객체

grouped_gender.get_group('female').head()

groupby 객체의 기초 연산 메소드

  • 그룹 데이터에 적용 가능한 통계 함수(NaN은 제외하여 연산)
  • count - 데이터 개수
  • sum - 데이터의 합
  • mean, std, var - 평균, 표준편차, 분산
  • min, max - 최소, 최대값

grouped_pclass.count()

groupby() 로 쪼갬 <-- split

group 별로 count()연산 <-- apply

DataFrame 으로 합하여 리턴 <-- combine (원본과는 다른 새로운 DataFrame)

grouped_pclass.sum()

객실 등급별 승객들의 나이 평균

grouped_pclass.mean()['Age']

퀴즈) 각 객실등급별 생존률 구하기

1 0.629630

2 0.472826

3 0.242363

grouped_pclass.mean()['Survived']

복수 columns로 groupping 하기

  • groupby에 column 리스트를 전달
  • 통계함수를 적용한 결과는 multiindex를 갖는 dataframe

'Pclass' x 'Sex'에 따른 생존률 구하기

grouped_multi = df.groupby(['Pclass', 'Sex'])
grouped_multi

grouped_multi.size()

grouped_multi.mean()

grouped_multi.mean().Survived

index를 이용한 group by

  • index가 있는 경우, groupby 함수에 level 사용 가능
    • level은 index의 depth를 의미하며, 가장 왼쪽부터 0부터 증가
  • set_index 함수
  • column 데이터를 index 레벨로 변경
  • reset_index 함수
  • 인덱스 초기화

df.head()

df.set_index(['Pclass', 'Sex'])

groupby(level), groupbt(함수)

index에 대해 groupby 수행

df.set_index('Embarked').groupby(level=0).mean()

이번에는 함수를 사용해보겠습니다 .

groupby(함수) =>

함수의 매개변수는 index 다

함수가 리턴하는 값이 grouping 기준이 된다!

나이대별로 생존률 구하기

df.set_index('Age')

df.set_index('Age').groupby(age_categorize)

df.set_index('Age').groupby(age_categorize)['Survived'].mean()

도전) 이름(Name) 시작 알파벳별로 승객수 집계

"""
A 51
B 72
C 69
D 43
...
...
U 1
V 15
W 33
Y 7
Z 3
"""
None

def pass_name(name) :
return name[0].upper()

df.set_index('Name').groupby(pass_name).size()

df.groupby(['Pclass', 'Sex']).sum()

df.groupby(['Pclass', 'Sex']).aggregate(np.sum)

df.groupby(['Pclass', 'Sex']).agg(np.sum)

df.groupby(['Pclass', 'Sex']).agg('sum') # 기본 집계함수는 문자열로 전달해도 동작한다.('sum', 'mean', 'count', ...)

◆ 경고가 뜬다. aggregate 하기 전에 애시당초 '집계' 안되는 컬럼은 drop 하고 진행하란다..

groupby 객체 뒤에 [컬럼] -> 각 그룹들(DataFrame) 에서 해당 컬럼만 추출한다

df.groupby(['Pclass', 'Sex']).agg(['mean', 'sum', np.max])

pivot, pd.pivot_table 함수

df = pd.DataFrame({
'지역': ['서울', '서울', '서울', '경기', '경기', '부산', '서울', '서울', '부산', '경기', '경기', '경기'],
'요일': ['월요일', '화요일', '수요일', '월요일', '화요일', '월요일', '목요일', '금요일', '화요일', '수요일', '목요일', '금요일'],
'강수량': [100, 80, 1000, 200, 200, 100, 50, 100, 200, 100, 50, 100],
'강수확률': [80, 70, 90, 10, 20, 30, 50, 90, 20, 80, 50, 10]
})
df

↑ 보기가 살짝 어렵다.. 왜?

지역별로 모아 보이면 좋겠는데..

요일별로 모아 보이면 좋겠는데...

보기 힘든 이유는, '지역' 과 '요일' 이 같은 컬럼 레벨에 있기 때문.

df.T # 차원 축이 바뀐것. 2차원에선 행과 열이 바뀜.

pivot()

  • dataframe의 형태를 변경
  • 인덱스, 컬럼, 데이터로 사용할 컬럼을 명시

pivot(index=None, columns=None, values=None)

index 로 지정할 컬럼

column 으로 지정할 컬럼

values 나머지 채울 값들.

df

df.pivot(index='지역', columns='요일')

df

df.loc[len(df)] = pd.Series(['경기', '금요일', 111, 11], index=["지역", "요일", "강수량", "강수확률"])
df

df.pivot('지역', '요일') # 인덱스는 id 처럼 겹치는값 x

df.drop([len(df) - 1], inplace=True)
df

df.pivot('요일', '지역', values='강수량')

df.pivot('요일', '지역')['강수량']

pd.pivot_table()

  • 기능적으로 pivot과 동일
  • pivot과의 차이점
    • 중복되는 모호한 값이 있을 경우, aggregation 함수 사용하여 값을 채움

pd.pivot_table(df, index='지역', columns='요일')

중복데이터를 만들어 본다. 서울-월요일

df2 = pd.DataFrame({
'지역': ['서울', '서울', '서울', '경기', '경기', '부산', '서울', '서울', '부산', '경기', '경기', '경기'],
'요일': ['월요일', '월요일', '수요일', '월요일', '화요일', '월요일', '목요일', '금요일', '화요일', '수요일', '목요일', '금요일'],
'강수량': [100, 80, 1000, 200, 200, 100, 50, 100, 200, 100, 50, 100],
'강수확률': [80, 70, 90, 10, 20, 30, 50, 90, 20, 80, 50, 10]
})

df2 # 서울 - 월요일 <-- 2개 있다.

df2.pivot('지역', '요일') # 에러 : Index contains duplicate entries (중복된 엔트리가 있다.)

pivot_table() 은 중복된 엔트리는 aggfunc= 를 사용하여 새로운 값 대체

서울 월요일은 '평균값' 으로 채워진다

df2.pivot_table(df2, index='지역', columns='요일', aggfunc=np.mean)

stack & unstack

  • stack : 컬럼 레벨에서 인덱스 레벨(row 레벨)로 dataframe 변경

  • 즉, 데이터를 쌓아올리는 개념으로 이해하면 쉬움

  • unstack : 인덱스 레벨에서 컬럼 레벨로 dataframe 변경

  • stack의 반대 operation

  • 둘은 역의 관계에 있음

df

new_df = df.set_index(['지역', '요일'])
new_df

unstack() : index(row) -> column

unstack(level=-1, fill_value=None)

level : 인덱스의 레벨

new_df 의 경우 지역 의 인덱스 레벨이 0 이다

-1 은 오른쪽 끝 인덱스 '요일' 이 컬럼이다.

new_df.unstack() # level -1을 컬럼레벨로 올림.

new_df.unstack(0) # levle = 0

df2 = new_df.unstack(0)
df2

stack() : column -> index(row)

stack(level=-1, dropna=True)

level : 컬럼의 레벨

df2.stack() # level = -1

df2.stack(0)

df2.stack()

df2.stack().stack() # 모든 컬럼이 인덱스로 내려오면 -> Series 객체 반환.

new_df

new_df.unstack('지역').stack() # 인덱스 '지역'과 '요일'을 바꿈.

pd.concat()

DataFrame 병합

pd.concat() 함수 사용하여 dataframe 병합하기

  • pandas.concat 함수
  • 축(axis)을 따라 dataframe을 병합 가능
    • axis = 0 → 행단위 병합 (디폴트)
    • axis = 1 → 열단위 병합

df1 = pd.DataFrame({'key1' : np.arange(10), 'value1' : np.random.randn(10)})
df2 = pd.DataFrame({'key1' : np.arange(10), 'value1' : np.random.randn(10)})
df1

df2

pd.concat([df1,df2]) # 행(row) 단위로 병합 (axis=0)

df1, df2의 컬럼이 같았다.

열 방향 (axis=1)으로 concat

pd.concat([df1,df2], axis=1)

df1과 df2의 index는 같았다.

column / index가 다른 경우

df3 = pd.DataFrame({'key2' : np.arange(10), 'value2' : np.random.randn(10)})
df3 # 컬럼명이 df1, df2 와는 다르다

pd.concat([df1, df3]) # 서로 없는 컬럼에는 NaN값으로 채워짐

pd.concat([df1,df3], axis=1)

df2

df2.index += 10
df2

pd.concat([df1,df2], axis = 1)

pd.merge(), join()

DataFrame의 병합과 조인

dataframe merge

  • SQL의 join처럼 특정한 column을 기준으로 병합
    • join 방식: how 파라미터를 통해 명시
      • inner: 기본값, 일치하는 값이 있는 경우
      • left: left outer join
      • right: right outer join
      • outer: full outer join
  • pandas.merge 함수가 사용됨

customer = pd.DataFrame({'customer_id' : np.arange(6),
'name' : ['철수'"", '영희', '길동', '영수', '수민', '동건'],
'나이' : [40, 20, 21, 30, 31, 18]})

orders = pd.DataFrame({'customer_id' : [1, 1, 2, 2, 2, 3, 3, 1, 4, 9],
'item' : ['치약', '칫솔', '이어폰', '헤드셋', '수건', '생수', '수건', '치약', '생수', '케이스'],
'quantity' : [1, 2, 1, 1, 3, 2, 2, 3, 2, 1]})

customer

orders

pd.merge(customer, orders, on = 'customer_id')

pd.merge(customer, orders, on = 'customer_id')

customer_id 컬럼을 기준으로 merge 됨.

customer_id 컬럼은 한번만 등장 (마치 Natural join)

customer 테이블에 있었던 '동건' '철수' 은 안보인다.

orders 테이블에 9번 customer 가 주문한 내용도 빠졌다.

왜? merge 조건에서 빠졌다.

기본적으로 inner join 이기에 조건에 맞지 않으면 빠지는 거다 (how="inner") (디폴트)

left outer join

pd.merge(customer, orders, on='customer_id', how='left') # left 는 customer

right outer join

pd.merge(customer, orders, on='customer_id', how='right')

full outer join

pd.merge(customer, orders, on='customer_id', how='outer') # outer = left + right

index 기준으로 merge

pd.merge?

cust1 = customer.set_index('customer_id')
cust1

order1 = orders.set_index('customer_id')
order1

'index 이름'이 같으면 굳이 on= 없이도 index기준으로 merge가능

pd.merge(cust1, order1, left_index=True, right_index=True)

가장 많이 팔린 아이템은??

orders

pd.merge(customer, orders, on='customer_id', how='right')

pd.merge(customer, orders, on='customer_id', how='right').groupby('item')['quantity'].sum()

pd.merge(customer, orders, on='customer_id', how='right').groupby('item')['quantity'].sum().sort_values(ascending=False)

pd.merge(customer, orders, on='customer_id', how='right').groupby('item')['quantity'].sum().sort_values(ascending=False)[:1]


join() 함수

  • 내부적으로 pd.merge 함수 사용
  • 기본적으로 index를 사용하여 left join (디폴트)

cust1.join(order1)

cust1.join(order1, how = 'inner')

profile
독해지자

0개의 댓글