
판다스(pandas)
판다스는 행(row), 열(column) 형태의 정형화된 2차원 데이터를 다루며, 엑셀의 Sheets, RDB의 Table이 그 예시입니다.
대표적인 판다스에서 제공하는 객체는 데이터프레임(DataFrame)과 시리즈(Series)가 있습니다.
먼저 판다스를 임포트합니다.
import pandas as pd시리즈(Series)
넘파이의 ndarray 기반으로 인덱싱 기능이 추가된 1차원 배열을 나타냅니다. Index 과 Value의 형태입니다.
인덱스(Index)를 지정하지 않을 시 기본적으로 ndarray와 같이 zero-based Index가 생성되며,
지정하면 명시적으로 지정된 Index를 사용합니다.
본격적으로, 판다스의 시리즈를 생성해보겠습니다.
s1 = pd.Series([10, 20, 30])
s1
각 시리즈에서의 속성을 알아보겠습니다.
첫번째, 시리즈에서의 값입니다.
s1.values # array([10, 20, 30])두번째, 시리즈에서의 인덱스입니다. 기본적으로 zero-based Index입니다.
s1.index # RangeIndex(start=0, stop=3, step=1)세번째, 시리즈에서의 데이터 타입입니다.
s1.dtype # dtype('int64')그래서 인덱스 속성을 통해 인덱싱 연산이 가능합니다.
s1[0] # np.int64(10)이번엔 data와 index를 명시하여 시리즈(Series)를 생성하는 방법을 알아보겠습니다.
s2 = pd.Series(data=[1, 2, 3], index=[100, 200, 300])
s2
그리고, index는 굳이 정수일 필요는 없습니다.
s3 = pd.Series(data=[1, 2, 3], index=['john', 'mark', 'charlie'])
s3
하지만 이럴 때 인덱싱은 인덱스 명칭을 제대로 부여해야 합니다.
s3['john'] # np.int64(1)그리고, 인덱싱은 중복을 허용합니다.
s4 = pd.Series(data=[1, 2, 3], index=['john','john','charlie'])
s4
이 때 중복된 인덱스인 john을 인덱싱하면 결과가 Series가 나옵니다. 차원 감소가 없습니다.
s4['john']
그리고, dict를 통해서도 Series 생성이 가능합니다.
dict의 key가 index로, value가 value로 맵핑됩니다.
sdata = {'김철수': 35000, '이상현': 67000, '최종수': 12000, '박진관': 4000}
s5 = pd.Series(sdata)
s5
Series의 인덱싱에 대해 자세히 알아보겠습니다. 아래와 같은 시리즈가 있다고 가정합시다.
s6 = pd.Series(data=np.arange(5), index=np.arange(100, 105), dtype=np.int16)
s6
이를 통해 이 Series의 Index 객체를 확인해보면 100, 101, 102, 103, 104일 것입니다.
s6.index # Index([100, 101, 102, 103, 104], dtype='int64')Index 객체에 values 속성으로 들어가면 ndarray 객체가 나옵니다.
s6.index.values # array([100, 101, 102, 103, 104])인덱싱 값을 통해 인덱싱 처리하면 0차원 스칼라 값이 나옵니다.
s6[102] # np.int16(2)리스트를 씌워 인덱싱 처리하면 1차원 배열 즉 Series가 나옵니다.
s6[[102]] # Series
value를 변경하고 싶다면 인덱싱 후 수정하면 됩니다.
s6[104] = 70
s6
기존에 없던 인덱스에 값을 대입하면 새로 생깁니다. (이것은 넘파이와 다른 점이며, Python의 dict와 유사합니다)
s6[105] = 90
s6
Series의 연산에 대해 알아보겠습니다. Series의 연산은 index 기준으로 연산합니다.
아래와 같이 서로 인덱스가 다른 Series의 예시를 살펴봅시다.
s1 = pd.Series(data=[1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
s2 = pd.Series(data=[6, 3, 2, 1], index=['d', 'c', 'b', 'a'])

이 상태에서 두 Series를 덧셈하면, 같은 인덱스끼리 덧셈하여, [a - 2, b - 4, c - 6, d - 10]이 출력됩니다.
s1 + s2
이와 같이 산술 연산은 인덱스가 일치한 스칼라와의 연산이 적용되고, 인덱스의 pair가 불일치하면 NaN이 됩니다.
s1 ** 2
s1 ** s2
그리고, Series에서의 불린 인덱싱(Boolean Indexing)도 넘파이의 불린 인덱싱(Boolean Indexing)과 동일한 효과입니다.
다음과 같은 Series를 하나 더 만듭니다.
s = pd.Series(np.arange(10), np.arange(10) + 1)
그리고 5보다 크고, 짝수인 Series를 골라내 보겠습니다.
s[(s > 5) && (s % 2 == 0)]
다음으로, Series 값을 삭제하기 위해서는 drop()을 사용합니다. (참고로 연산 자체는 원본 값에 변화는 없습니다)
하지만, inplace=True 옵션을 주면 원본 변화시키도록 동작합니다.
s.drop(9, inplace=True)
Series에서는 인덱스(Index)에 다중 레벨(Multi-Level)을 가질 수 있습니다.
다중 레벨 인덱스(Multi-Level Index)를 가진 Series를 만들어보겠습니다.
s3 = pd.Series(data=np.arange(100, 106), index=[
['a', 'a', 'b', 'b', 'c', 'c'],
[1, 2, 1, 2, 1, 2]
])
인덱스에 접근해보면, MultiIndex 객체로 구성됩니다.
s3.index # MultiIndex([('a', 1), ('a', 2), ('b', 1), ('b', 2), ('c', 1), ('c', 2)])따라서, 튜플 인덱싱으로 value 접근이 가능합니다.
s3['a', 1] # np.int64(100)추가로, apply() 함수는 Series의 각 value 원소에 함수를 적용시킬 때 사용하며, 일종의 원소별 변환 역할입니다.
다음과 같은 zero-based Index의 Series가 있다고 가정합시다.
s = pd.Series([1, 2, 3, 4, 5], dtype=int16)각 원소에 제곱을 적용하기 위해서 apply()에 람다 함수를 적용합니다.
result = s.apply(lambda x: x**2)사용자 정의 함수를 만들어 추가 인자를 전달할 수도 있습니다.
def multiply(x, factor):
return x * factor
result = s.apply(multiply, args=(10,))데이터프레임(DataFrame)
Series가 1차원이라면, DataFrame은 2차원으로 확대된 버젼이며, 2차원이므로 Index가 row, column으로 구성됩니다.
row는 각 개별 데이터를 column은 데이터의 개별 속성(feature)을 의미합니다.
데이터 분석/머신 러닝에서 데이터 변형을 위해서 많이 사용하는 개념입니다.
먼저 csv 파일을 읽기 위해 메인 폴더 path를 불러옵니다.
base_path = r'c:\Users\haruka\Desktop\AI수업\mcp-month-2\data'분석할 csv에 해당하는 파일을 가져옵니다.
filepath = os.path.join(base_path, 'titanic.csv')read_csv()를 통해 DataFrame 객체로 읽어옵니다.
df = pd.read_csv(filepath)
이 데이터프레임은 자주 이용할 것이므로, 함수로 만들어둡니다.
def load_titanic():
return pd.read_csv(filepath)함수를 불러와 DataFrame을 출력하면, 0 ~ 890까지의 zero-based Index와 12개의 column을 가집니다.
df = load_titanic()각 컬럼의 의미는 다음과 같습니다.
- passengerId: 여권 ID
- survived : 살아남았는지 여부 (0: 살아남지 못함, 1: 살아남음)
- Pclass : 여권의 클래스 (1 = 1st, 2 = 2nd, 3 = 3rd)
- Name : 승객 이름
- Sex: 승객 성별
- Age: 승객 나이
- SibSp: 탑승한 배우자들 혹은 형제들
- ParCh: 탑승한 부모 혹은 자녀들
- Ticket: 티켓 넘버
- Fare: 여권 요금
- Cabin: 선실
- Embarked : 출발지 혹은 승선 탑승지 (C: Cherbourg, Q: Queenstown, S: Southampton)
추가적으로, df의 type을 확인하면 DataFrame 객체로 나옵니다.
type(df) # pandas.DataFramehead()를 통해 상위 N개의 행(row)을 확인 가능합니다. 기본값은 5 입니다.
df.head()
tail()을 통해 하위 N개의 행(row)을 확인 가능합니다. 기본값은 5입니다.
df.tail()
shape 속성을 통해 2차원 DataFrame의 row와 column을 확인합니다.
df.shape # (891, 12)info()를 통해 각 컬럼별 타입과 Not Null인 값의 개수를 확인합니다.
df.info()
describe()에 대해 숫자형 데이터에 대해 기술통계량 정보를 알려줍니다.
count(개수), mean(평균값), std(표준편차), min(최소값), max(최대값), 50%(중위), 25%, 75% 값을 제공합니다.

DataFrame 객체에도 Index 속성이 존재합니다. 각 아이템을 특정할 수 있는 고유의 값을 저장합니다.
Series에서처럼 복잡한 데이터의 경우 MultiIndex로 표현할 수 있습니다.
df.index # RangeIndex(start=0, stop=891, step=1)DataFrame 객체에는 columns 속성이 존재합니다. 각각의 특성(features)를 나타냅니다.
복잡한 데이터의 경우 MultiColumn으로 표현 가능합니다.
컬럼은 axis = 1 레벨의 Index이므로 Index 객체로 표현됩니다.
컬럼은 feature(특성), 속성, 필드, 차원 같은 용어로 표현합니다.
df.columns
# Index(['PassengerId', 'Survived', .... , 'Embarked'], dtype='object')데이터프레임 수동 생성하기
DataFrame은 리스트, 튜플, ndarray와 같은 다양한 객체로부터 수동 생성할 수 있습니다.
리스트를 통해 데이터프레임 객체를 만들어보겠습니다.
df = pd.DataFrame([1, 2, 3])
결과적으로 리스트의 요소인 1, 2, 3은 각각의 행 데이터로 들어가고 Index와 Columns가 알아서 생성됩니다.
그리고, 아래와 같이 2차원 리스트로 데이터를 넣으면, 순서대로 첫번째 행, 두번째 행 데이터로 들어갑니다.
df = pd.DataFrame([
[1, 2, 3],
[4, 5, 6]
])
이에 따라 여러가지 속성들을 가지고 있는데, len()을 통해 행의 개수(axis=0)를 알아봅니다.
len(df) # 2shape는 DataFrame의 row, column을 튜플 형식으로 알 수 있습니다.
df.shape # (2, 3)ndim()은 이 DataFrame의 차원의 개수를 알려줍니다.
df.ndim # 2size는 가지고 있는 스칼라 값의 개수를 알려줍니다.
df.size # 6dtypes는 각 column 별 데이터 타입이 시리즈 형식으로 나옵니다.
df.dtypes
추가로, DataFrame을 생성할 때 Columns를 지정해줄 수 있습니다.
df2 = pd.DataFrame([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
], columns=['a', 'b', 'c', 'd'])
Columns 다른 값으로 변경할 수도 있습니다.
df2.columns = ['국어', '영어', '수학', '과학']
같은 원리로 Index도 다른 값으로 변경할 수도 있습니다.
df2.index = ['고길동', '둘리', '마이콜']
이번에는 dict 자료구조로 DataFrame을 만들어보겠습니다. 다음과 같은 dict 변수가 있다고 해봅시다.
data = { 'a': [100], 'b': [200], 'c': [300] }이것을 통해 만들면, key는 columns에 values는 값에 대응됩니다.
df3 = pd.DataFrame(data)
또한, dict 변수를 이용해 Index 및 Columns의 이름을 바꿀수도 있습니다.
df3.rename(columns={ 'a': 'kor', 'b': 'sci' },index={0: 'torch'}, inplace=True)
reset_index()를 사용해 기존의 Index를 column 레벨로 올리고, 새로운 Index를 붙이는 방식도 있습니다.
df3.reset_index()
여기서 drop=True 옵션을 추가하면, 기존의 index는 사라지고, zero-based Index로 초기화됩니다.
df3.reset_index(drop=True)
set_index()를 사용해 특정 column을 Index로 내리고, 기존의 Index를 대체하는 방법도 있습니다.
parameter로 컬럼명을 붙이면 됩니다.
df3.set_index('kor')
멀티 레벨 인덱스와 멀티 레벨 컬럼
먼저, 아래와 같은 코드로 DataFrame을 생성해주세요.
df4 = 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=[['서울', '서울', '경기', '경기'], ['평일', '휴일', '평일', '휴일']]
)
결과를 보면 알 수 있듯이, key는 column에 대응되는데 tuple 형식으로 되어 있어, 2-level column으로 구성됩니다.
또한, Index는 2차원 데이터로 구성되어 2-level index로 구성됩니다.
특정 컬럼 선택하기
앞서 함수로 만들어 두었던 데이터 불러오기 함수를 실행합니다.
df = load_titanic()
여기서 특정 컬럼을 선택하려고 하면, [ ] 안에 컬럼 이름을 명시해주면 됩니다. 결과는 Series가 나옵니다.
혹은 컬럼명에 닷(.) 연산자를 통해 접근해도 됩니다.
df['Survived']
df.Survived
혹은 결과가 DataFrame으로 나오게 하기 위해서는 [ ] 안에 리스트를 전달하면 됩니다.
df[['Survived']]
같은 원리로 복수 컬럼을 DataFrame으로 표시하려면 콤마(,)로 구분하면 됩니다.
df[['Survived', 'Age', 'Name']]
특정 행 선택하기
행을 선택할 때는 loc[]와 iloc[] 속성을 사용합니다.
예제를 이해하기 쉽게 돕기 위해 임의적으로 zero-based Index에 100을 더하겠습니다.
df.index += 100
이제 loc를 사용해 접근할 때는 [ ] 안에 그 행의 인덱스를 적어주면 됩니다. 결과는 Series 입니다.
df.loc[986]
위에서 살펴본 원리로 [ ] 안에 리스트를 넣으면, 차원 변화가 없으므로 DataFrame이 나옵니다.
df.loc[[986]]
역시 여러 개의 행을 얻기 위해서는 콤마(,)로 구분하여 Index를 적어주면 됩니다.
df.loc[[986, 100, 110, 990]]
반대로 iloc는 Index의 번호가 아니라 Index의 순서 개념으로 접근합니다.
예를 들어, 0번째 행의 Index를 가져오겠다고 하면, loc는 반드시 첫번째 Index 번호가 0이어야 합니다.
하지만, iloc는 순서로 접근하는 것이기 때문에 몇번째 순서인지만 생각하면 됩니다.
df.iloc[0]
같은 원리로, 리스트로 인덱싱하면 DataFrame을 얻을 수 있습니다.
df.iloc[[0, 100, 200, 2]]
추가로 이야기할 점은 위에서 말한 loc[], iloc[]는 행 선택 뿐만 아니라, 컬럼 선택할 때도 사용할 수 있다는 점입니다.
즉, 행과 컬럼을 동시에 선택할 때 tuple을 이용하여 row, column 둘다 명시 가능합니다.
loc의 경우 loc[axis=0 row, axis=1 column] 형태로 명령하고,
iloc는 iloc[zero based row, zero based column] 형태로 명령합니다.
loc 먼저 살펴보겠습니다.
# 986번 인덱스, Survived 컬럼 선택
df.loc[986, 'Survived'] # np.int64(0)
iloc의 경우도 살펴보겠습니다.
# 뒤에서 5번째 인덱스, 뒤에서 3번째 컬럼
df.iloc[-5, -3]
행 및 컬럼 추가 및 삭제
컬럼을 추가하기 위해서는 [ ]을 이용해 추가하거나, insert()를 이용해 원하는 위치에 추가할 수 있습니다.
[ ]를 이용한 방법을 사용하여 기존 컬럼을 이용한 파생 변수를 만들 때, 아래와 같이 작성합니다.
df['Age_Double'] = df['Age'] * 2
반대로, 원하는 위치에 컬럼 추가를 하려면, insert()를 사용해야 합니다.
df.insert(3, 'Fare10', df['Fare'] / 10)
컬럼을 삭제하기 위해서는 drop()을 이용하여 삭제합니다.
여기서 행(axis=0)을 삭제할지, 열(axis=1)을 삭제할지 명시해주어야 합니다.
df.drop(['Age Double', 'Fare10'], axis=1, inplace=True)반대로, 행을 추가하기 위해서는 loc[] 문법을 사용합니다. 확인을 위해 새로운 DataFrame을 만듭니다.
df2 = pd.DataFrame([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
], columns=['a', 'b', 'c', 'd'], index=['김길동', '최진수', '허수민'])
새로운 행을 추가하기 위해 김갑수 행을 추가하겠습니다. 다만, pd.Series를 이용할 경우 Index를 맞춰주어야 합니다.
df2.loc['김갑수'] = pd.Series([100, 200, 300, 400], index=['a', 'b', 'c', 'd'])
df2.loc['김철수'] = [101, 202, 303, 404]
상관관계(Correlation)
정형 데이터에선 여러 개의 컬럼이 있는데 각 컬럼 간의 상관관계가 있을 수도 있습니다.
상관관계라는 것은 두 변수간의 흐름이 얼마나 비슷한가를 나타내는 척도입니다.
가령 a 컬럼의 값이 증가할 때 b 컬럼의 값이 증가 혹은 감소하는지 파악하여 그렇다면 상관관계가 있는 것입니다.
즉, 증감의 성향이 비슷한가를 보는 것입니다. 그러면서, 이 상관관계의 값을 -1 ~ +1 사이로 표현합니다.
+1에 가까울수록 둘은 비슷하게 증감하고, -1에 가까울수록 둘은 반대로 증감합니다.
또한, 절댓값 0에 가까우면 상관관계가 적습니다. 다른 말로 이것을 피어슨 상관계수라고도 합니다.
판다스에서는 corr() 함수를 통해 상관계수를 연산합니다.
데이터 타입이 숫자 타입인 경우에만 확인하겠습니다.
df.corr(numeric_only=True)
위 결과를 보면, 2개의 컬럼 간의 상관관계를 보는 것이므로, 주대각선은 모두 +1 값을 가집니다.
여기서는 Fare와 Pclass가 상대적으로 절댓값이 높은 분포를 가집니다. Pclass가 작아질수록 Fare는 올라갑니다.
NaN 및 결측값 처리
데이터 분석할 때 NaN(Not a Number) 혹은 결측값(Missing Value) 그대로 둔 채 분석을 하면
나중에 각종 집계 동작 부분에서 문제가 발생할 수 있습니다.
보통 데이터 분석 전에는 NaN 값을 무시하던지, 다른 값으로 채우든지 같은 처리를 사용합니다.
먼저 NaN 값을 확인하기 위해 info() 함수를 통해 확인할 수 있습니다.
df.info() # Age, Cabin, Embarked
혹은 isna() 함수를 통해 True인 부분이 결측치 부분임을 확인할 수 있습니다.
df.isna()
특정 컬럼 (예시에서는 Age 컬럼)만 결측치를 확인하려면 Series에 접근한 후 isna()를 실행합니다.
df['Age'].isna()
여기서 결측된 데이터만 확인하려면 Boolean Selection을 활용합니다. Age 컬럼에서 결측치인 부분만 뽑아냅니다.
df[df['Age'].isna()]
이제 NaN 값을 삭제해보겠습니다. 이 때는 dropna() 함수를 이용합니다.
이것은 행(row) 기준으로 하나라도, NaN 값이 있으면 그 행(row)을 무조건 삭제하므로,
특정 컬럼 (예시에서는 Age)만 삭제해보겠습니다.
원래 891개의 행에서 714개의 행으로 줄어들게 됩니다.
df.dropna(subset=['Age'])
반대로, NaN이 있는 컬럼 자체를 삭제하려면 axis=1으로 설정해주어야 합니다.
여기서는 Age, Cabin, Embarked 컬럼이 삭제될 것입니다.
df.dropna(axis=1)다른 방법으로 NaN 값을 대체하는 방법도 있습니다. 그 때 사용하는 함수가 fillna() 입니다.
여기서는 예시로 Age의 NaN 값을 대체하는데, Age의 평균값으로 대체해보겠습니다.
Age의 평균값은 다음과 같이 계산합니다.
df['Age'].mean() # np.float64(29.699....)하지만 이것은 전체 승객에 대한 평균이 아니라, NaN 값을 배제한 평균을 냅니다.
실제 count() 함수를 통해 적용한 승객의 명수를 확인해봅니다.
df['Age'].count() # np.int64(714)그래서 이 값을 NaN 값 대신 대체합니다.
df = df['Age'].fillna(df['Age'].mean())수치형 데이터 & 범주형 데이터
수치형 데이터는 데이터의 특성이 연속성을 띄는 숫자로 이루어진 데이터를 말합니다.
그리고, 대소비교가 가능하고 산술연산(평균, 합계, 중간값)이 의미가 있어야 합니다.
범주형(분류형) 데이터는 연속적이지 않은 값(대부분 숫자를 제외한 나머지)을 갖는 데이터를 의미합니다.
어떤 경우, 데이터 타입이 숫자형이어도 개념적으로 범주형으로 처리해야 할 경우도 있습니다.
예를 들어, 대한민국 우편번호는 숫자로 이루어져 있습니다.
역삼동은 06240이고, 애월읍은 63131입니다. 그러면, 애월읍은 역삼동의 약 10.11배라고 표현합니까?
이런 산술 연산 비교가 의미가 없으므로, 범주형 데이터로 분리합니다.
따라서, 대소비교가 불가하고, 산술연산(평균, 합계, 중간값)이 의미가 없어야 합니다.
그래서 이번에는 Age 컬럼의 데이터를 10대, 20대, 30대 이런 식으로 범주형 데이터로 변환해보겠습니다.
apply() 함수를 통해 맵핑해야 하므로, 먼저 함수를 정의합니다.
def age_categorize(age):
if np.isnan(age):
return -1
return int(age / 10) * 10그리고, 특정 컬럼(현재는 Age 컬럼)에 함수를 apply() 합니다.
age_span = df['Age'].apply(age_categorize)
분류형 데이터로 제대로 구분되었는지 unique() 통해 확인합니다.
age_span.unique() # array([20, 30, -1, 50, 0, 10, 40, 60, 70, 80])또, 각각의 나이대별로 몇개의 데이터가 있는지 value_counts()를 통해 확인합니다.
age_span.value_counts()
위 정보를 파생변수로 따로 저장합니다.
df['Age_span'] = age_span
원-핫 인코딩 (One-Hot Encoding)
범주형 데이터를 수치화 해야할 필요가 있을 때 원-핫 인코딩(One-Hot Encoding)을 사용합니다.
가령 어떤 컬럼의 데이터가 Red, Green, Blue 3가지가 있다고 가정해보겠습니다.
기본적으로 범주형 데이터는 연산이 불가합니다. 따라서 연산 처리가 가능하도록 데이터를 바꿔주어야 합니다.
범주형 데이터의 컬럼 1개를 범주의 개수만큼 늘려서 범주에 해당하는 값에만 1을 주고, 나머지는 0을 주어 처리합니다.
3가지 범주값이 있는 현재 상황에서, Red / Green / Blue 3개의 컬럼으로 만들어
Red는 Red일 경우에만 1, 나머지는 0 / Green은 Green일 경우에만 1, 나머지는 0 / Blue는 Blue일 경우에만 1, 나머지는 0
이런 방식으로 변환시키는 겁니다.
위에서 살펴본 데이터에서는 Pclass 데이터를 예시로 잡겠습니다. 먼저 유니크한 값들이 몇개인지 확인합니다.
df['PClass'].unique() # array([3, 1, 2])총 3가지의 범주값이 나오는데, 판다스에서 get_dummies() 함수를 이용해
범주형 데이터에 대해 원-핫 인코딩을 수행합니다.
다만 여기서는 Pclass, Sex, Embarked 컬럼에만 지정합니다.
그리고, drop_first=True 옵션을 통해 첫번째 컬럼은 제외시킵니다.
pd.get_dummies(df, columns=['Pclass', 'Sex', 'Embarked'], dtype=int, drop_first=True)
이렇게 하면 범주형 데이터도 연산이 가능한 형태가 되었다고 했는데
그 의미는 머신러닝을 하다보면, 범주형 데이터에 대한 예측값이 [1, 0, 0] 이런식으로 정확히 나오지 않습니다.
수많은 연산을 통해 [0.982, 0.0003, 0.0004] 이런 식으로 나오는데
[1, 0, 0] / [0, 1, 0] / [0, 0, 1] 3개 중 무엇에 가까운지 판단할 수 있는데 이것도 산술 연산 과정인 것입니다.
그룹화 - groupby
DataFrame에 있는 데이터들을 특정 조건에 맞게 묶어서 그룹화하는 과정입니다.
먼저, groupby()에서 by parameter로 컬럼이나 컬럼의 리스트를 넣으면, 해당 컬럼의 값으로 쪼갭니다.
grouped_pclass = df.groupby(by='Pclass') # DataFrameGroupBy object이 함수는 DataFrameGroupBy 객체가 리턴이 되는데, 이 객체는 groups 속성 및 size 함수를 가집니다.
첫번째 groups 라는 속성은 dict 형태로 구성이 되는데 key 값이 Pclass의 값들이고, value는 Index의 리스트입니다.
grouped_pclass.groups
두번째 size 라는 함수는 각 값들에 따라 몇개인지 확인하는 방법입니다. value_counts()와 같은 효과입니다.
grouped_pclass.size()
추가로, get_group()은 특정 그룹의 DataFrame을 리턴해줍니다.
예시로, Pclass가 1인 그룹의 DataFrame을 출력해봅시다.
grouped_pclass.get_group(1)
이 그룹화 데이터를 가져오면, 기초 연산을 할 수 있는 각종 집계 연산 메소드를 제공합니다.
첫번째는 count() 메소드입니다. 그룹화한 데이터에 대해 각 컬럼 데이터의 개수를 합친 DataFrame으로 보여줍니다.
데이터 분석에서는 이러한 것을 split-apply-combine 이라고 합니다.
grouped_pclass.count()
두번째는 sum() 메소드입니다. 그룹화한 데이터에 대해 각 데이터의 총합을 보여줍니다.
하지만, 숫자형 데이터만 연산해야 하므로, numeric_only=True 옵션을 추가해줍니다.
grouped_pclass.sum(numeric_only=True)
세번째는 mean() 메소드입니다. 평균값을 보여줍니다. 하지만 문자열 타입이 아니라 숫자형 타입만 계산해야 하므로
이것도 numeric_only=True 옵션을 추가해야 합니다.
grouped_pclass.mean(numeric_only=True)
혹은 각 그룹별 컬럼을 선택해서 평균값을 출력할수도 있습니다.
grouped_pclass['Age'].mean()
혹은 여러 개의 그룹을 선택하면 DataFrame 형태로 출력됩니다.
grouped_pclass[['Age', 'Survived']].mean()
이번엔 복수의 컬럼으로 grouping하는 방법입니다. groupby에 column 리스트를 전달하면 됩니다.
통계 함수를 전달한 최종 결과는 MultiIndex를 가지는 DataFrame이 됩니다.
예를 들어 Pclass와 Sex에 따른 생존율(Survived)을 구하고 싶다고 해봅시다.
grouped_multi = df.groupby(['Pclass', 'Sex'])어떻게 구성되어 있는지 확인을 위해 groups 속성으로 접근하면 dict key 값이 tuple입니다.
총 2 x 3 = 6개의 경우의 수가 나옵니다.
grouped_multi.groups
size() 함수로 확인하면, Index가 2-Level MultiIndex 입니다.
grouped_multi.size()
최종적으로 그룹별 생존율(Survived) 평균값(mean)은 아래와 같이 확인합니다.
grouped_multi[['Survived']].mean()
이번에는 Index Level을 설정해 Index별로 groupby 하는 방법입니다.
먼저 현재 데이터를 확인해보겠습니다.
df.head()
현재 Index는 zero-based Index 입니다. 여기서 Pclass column 데이터를 Index로 내리기 위해 아래와 같이 합니다.
df.set_index('Pclass')
이 상태에서 추가로, Pclass와 Sex로 Index를 설정합니다.
df.set_index(['Pclass', 'Sex'])
여기서 다른 기준으로 Index를 만들기 위해 다시 zero-based Index로 바꿉니다.
df.set_index(['Pclass', 'Sex']).reset_index()
이제 Enbarked 컬럼을 레벨 0 Index로 만든 후 그 기준으로 groupby 하여 size()를 확인합니다.
df.set_index('Embarked').groupby(level=0).size()
다음으로, groupby에 함수를 전달하여 리턴하는 값을 grouping 기준으로 삼을 수 있습니다.
함수의 매개변수는 각 행의 Index를 받아야 합니다.
먼저 이전에 만들어둔 age_categorize() 함수를 참고해봅시다.
def age_categorize(age):
if np.isnan(age):
return -1
return int(age / 10) * 10여기서 Age 컬럼 데이터에 대해 age_categorize 기준으로 grouping 합니다.
df.set_index('Age').groupby(age_categorize).size()