Pandas 패키지 소개
Pandas 패키지는 데이터 분석을 할 때 가장 많이 쓰이는 패키지이다. 대부분의 데이터는 시계열(series)이나 표(table)의 형태로 나타낼 수 있는데 Pandas에서는 이러한 표 데이터를 다루기 위한 시리즈(Series) 클래스와 데이터프레임(DataFrame) 클래스를 제공한다.
Series
Series는 numpy에서 제공하는 1차원 배열과 비슷하지만 각 데이터의 의미를 표시하는 인덱스(index)를 붙일 수 있다. 데이터 자체는 값(Value)라고 한다.
Series = Value(값) + Index(인덱스)
import pandas as pd
Series 생성
s = pd.Series([9904312, 3448737, 2890451, 2466052], #값
index=["서울","부산","인천","대구"]) #인덱스
s
서울 9904312
부산 3448737
인천 2890451
대구 2466052
dtype: int64
만약 인덱스 지정 없이 Series를 만들면 Series의 인덱스는 0부터 시작하는 정수값이 된다.
pd.Series(range(10,14))
0 10
1 11
2 12
3 13
dtype: int64
s.index
Index(['서울', '부산', '인천', '대구'], dtype='object')
s.values
array([9904312, 3448737, 289511, 264544], dtype=int64)
name
: Series 데이터의 이름index.name
: Series 인덱스의 이름
s.name="인구"
s.index.name = "도시"
s
도시
서울 9904312
부산 3448737
인천 289511
대구 264544
Name: 인구, dtype: int64
Series 연산
연산은 Series의 값에만 적용되며 인덱스 값은 변하지 않는다.
s/1000000
서울 9.904312
부산 3.448737
인천 2.890451
대구 2.466052
dtype: float64
Series 인덱싱
인덱스 라벨을 이용한 인덱싱 또는 배열 인덱싱을 이용한 슬라이싱이 가능하다.
s[1], s["부산"]
(3448737, 3448737)
배열 인덱싱을 하면 자료의 순서를 바꾸거나 특정 라벨의 자료만 선택할 수 있다.
s[["서울","대구"]]
서울 9904312
대구 2466052
dtype: int64
s[(250e4 < s) & (s < 500e4)]
부산 3448737
인천 2890451
dtype: int64
print(s)
s[1:3]
서울 9904312
부산 3448737
인천 2890451
대구 2466052
dtype: int64
부산 3448737
인천 2890451
dtype: int64
만약 라벨 값이 영문인 경우에는 마치 속성처럼 점(.)을 이용하여 접근할 수도 있다.
s0 = pd.Series(range(3), index=["a", "b", "c"])
s0
a 0
b 1
c 2
dtype: int64
(s0.a, s0.b)
(0, 1)
Series와 Dictionary 자료형
Series 객체는 라벨 값에 의해 인덱싱이 가능하므로 실질적으로 라벨 값을 키(key)로 가지는 Dictionary 자료형과 같다고 볼수 있다. 따라서 Dictionary 자료형에서 제공하는 in 연산도 가능하고 items 메서드를 이용하면 for 루프를 통해 각 원소의 key와 value에 접근할 수도 있다.
"서울" in s
True
"대전" in s
False
for k, v in s.items():
print("%s = %d" % (k, v))
서울 = 9904312
부산 = 3448737
인천 = 2890451
대구 = 2466052
또 Dictionary 객체에서 시리즈를 만들수 있다.
s2 = pd.Series({"서울": 9631482, "부산": 3393191, "인천": 2632035, "대전": 1490158})
s2
서울 9631482
부산 3393191
인천 2632035
대전 1490158
dtype: int64
Dictionary의 원소는 순서를 가지지 않으므로 Series의 데이터에도 순서가 보장되지 않는다. 만약 순서를 정하고 싶다면 인덱스를 리스트로 지정해야 한다.
s2 = pd.Series({"서울": 9631482, "부산": 3393191, "인천": 2632035, "대전": 1490158},
index=["부산", "서울", "인천", "대전"])
s2
부산 3393191
서울 9631482
인천 2632035
대전 1490158
dtype: int64
인덱스 기반 연산
- s : 2015년 인구 증가
- s2 : 2010년 인구 증가
ds = s - s2
ds
대구 NaN
대전 NaN
부산 55546.0
서울 272830.0
인천 258416.0
dtype: float64
s.values - s2.values
array([ 6511121, -6182745, 258416, 975894], dtype=int64)
대구와 대전의 경우 s, s1 모두에 준재하지 않기 때문에 계산이 불가능하므로 NaN
(Not a Number)값을 가지게 된다. 또한 NaN
값이 float
자료형에서만 가능하므로 다른 계산 결과도 모두 float
자료형이 되었다. NaN
이 아닌 값을 구하려면 notnull
메서드를 사용하야 한다.
ds.notnull()
대구 False
대전 False
부산 True
서울 True
인천 True
dtype: bool
ds[ds.notnull()]
부산 55546.0
서울 272830.0
인천 258416.0
dtype: float64
인구 증가율(%)은 다음과 같이 구할 수 있다.
rs = (s-s2)/s2 * 100
rs = rs[rs.notnull()]
rs
부산 1.636984
서울 2.832690
인천 9.818107
dtype: float64
데이터 갱신, 추가, 삭제
인덱싱을 이용하면 딕셔너리처럼 데이터를 갱신하거나 추가 할 수 있다.
rs["부산"] = 1.63
rs
부산 1.630000
서울 2.832690
인천 9.818107
dtype: float64
rs["대구"] = 1.41
rs
부산 1.630000
서울 2.832690
인천 9.818107
대구 1.410000
dtype: float64
del rs["서울"] # 삭제
rs
부산 1.630000
인천 9.818107
대구 1.410000
dtype: float64
DataFrame
시리즈가 1차원 벡터 데이터에 행방향 인덱스(row index)를 붙인 것이라면 데이터프레임 클래스는 2차원 행렬 데이터에 인덱스를 붙인 것과 비슷하다. 2차원이므로 각각의 행 데이터의 이름이 되는 행방향 인덱스(row index) 뿐 아니라 각각의 열 데이터의 이름이 되는 열방향 인덱스(column index)도 붙일 수 있다.
DataFrame 생성
- 하나의 열이 되는 데이터를 리스트나 일차원 배열로 준비
- 이 각각의 열에 대한 이름(라벨)을 키로 가지는 딕셔너리 생성
- 이 데이터를 DataFrame 클래스 생성자에 넣고 열방향 인덱스는
columns
, 행방행 인덱스는index
인수로 지정
data = {
"2015": [9904312, 3448737, 2890451, 2466052],
"2010": [9631482, 3393191, 2632035, 2431774],
"2005": [9762546, 3512547, 2517680, 2456016],
"2000": [9853972, 3655437, 2466338, 2473990],
"지역": ["수도권", "경상권", "수도권", "경상권"],
"2010-2015 증가율": [0.0283, 0.0163, 0.0982, 0.0141]
}
columns = ["지역", "2015", "2010", "2005", "2000", "2010-2015 증가율"]
index = ["서울", "부산", "인천", "대구"]
df = pd.DataFrame(data, index=index, columns=columns)
df
지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 | |
---|---|---|---|---|---|---|
서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 0.0283 |
부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 0.0163 |
인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 0.0982 |
대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 0.0141 |
앞에서 데이터프레임은 2차원 배열 데이터를 기반으로 한다고 했지만 사실은 공통 인덱스를 가지는 열 시리즈(column series)를 딕셔너리로 묶어놓은 것이라고 보는것이 더 정확하다. 2차원 배열 데이터는 모든 원소와 같은 자료형을 가져야 하지만 데이터프레임은 각 열(column)마다 자료형이 다를 수 있기 때문이다. 위 예제에서도 지역과 인구와 증가율은 각각 문자열, 정수, 부동소수점 실수 이다.
시리즈와 마찬가지로 데이터만 접근하려면 values
속성을 사용하고, 열방향 인덱스와 행방향 인덱스는 각각 columns
, index
속성으로 접근한다.
df.values
array([['수도권', 9904312, 9631482, 9762546, 9853972, 0.0283],
['경상권', 3448737, 3393191, 3512547, 3655437, 0.0163],
['수도권', 2890451, 2632035, 2517680, 2466338, 0.0982],
['경상권', 2466052, 2431774, 2456016, 2473990, 0.0141]], dtype=object)
df.columns
Index(['지역', '2015', '2010', '2005', '2000', '2010-2015 증가율'], dtype='object')
df.index
Index(['서울', '부산', '인천', '대구'], dtype='object')
시리즈에서 처럼 열방향 인덱스와 행방향 인덱스에 이름을 붙일수 있다.
df.index.name = "도시"
df.columns.name = "특성"
df
특성 | 지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 |
---|---|---|---|---|---|---|
도시 | ||||||
서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 0.0283 |
부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 0.0163 |
인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 0.0982 |
대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 0.0141 |
데이터 프레임은 전치(transpose)를 포함하여 2차원 numpy 배열이 가지는 대부분의 속성이나 메서드를 지원한다.
df.T
도시 | 서울 | 부산 | 인천 | 대구 |
---|---|---|---|---|
특성 | ||||
지역 | 수도권 | 경상권 | 수도권 | 경상권 |
2015 | 9904312 | 3448737 | 2890451 | 2466052 |
2010 | 9631482 | 3393191 | 2632035 | 2431774 |
2005 | 9762546 | 3512547 | 2517680 | 2456016 |
2000 | 9853972 | 3655437 | 2466338 | 2473990 |
2010-2015 증가율 | 0.0283 | 0.0163 | 0.0982 | 0.0141 |
열 데이터의 갱신, 추가, 삭제
데이터프레임은 열 시리즈의 딕셔너리로 볼수 있으므로 열 단위로 데이터를 갱신하거나 추가, 삭제할 수 있다.
df["2010-2015 증가율"] = df["2010-2015 증가율"] * 100
df
지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 | |
---|---|---|---|---|---|---|
서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 2.83 |
부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 1.63 |
인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 9.82 |
대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 1.41 |
df["2005-2010 등가율"] = ((df["2010"] - df["2005"])/df["2005"]*100).round(2)
df
지역 | 2015 | 2010 | 2005 | 2000 | 2010-2015 증가율 | 2005-2010 등가율 | |
---|---|---|---|---|---|---|---|
서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | 2.83 | -1.34 |
부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | 1.63 | -3.40 |
인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 9.82 | 4.54 |
대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | 1.41 | -0.99 |
del df["2010-2015 증가율"]
df
지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |
---|---|---|---|---|---|---|
서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | -1.34 |
부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | -3.40 |
인천 | 수도권 | 2890451 | 2632035 | 2517680 | 2466338 | 4.54 |
대구 | 경상권 | 2466052 | 2431774 | 2456016 | 2473990 | -0.99 |
열 인덱싱
데이터프레임은 열 시리즈의 딕셔너리와 비슷하다고 하였다. 따라서 데이터프레임을 인덱싱 할 때도 열 라벨(column label)을 키값으로 생각하여 인덱싱을 할 수 있다. 인덱스로 라벨을 넣거나 배열형태로 넣는것도 가능하다.
df["지역"] # Series 반환
서울 수도권
부산 경상권
인천 수도권
대구 경상권
Name: 지역, dtype: object
pandas.core.series.Series
df[["2010", "2015"]]
2010 | 2015 | |
---|---|---|
서울 | 9631482 | 9904312 |
부산 | 3393191 | 3448737 |
인천 | 2632035 | 2890451 |
대구 | 2431774 | 2466052 |
만약 하나의 열만 빼서 데이터프레임 자료형을 유지하고 싶다면 원소가 하나인 리스트를 써서 인덱싱 하면된다.
df[["2010"]] # type(df[["2010"]]) => pandas.core.frame.DataFrame
2010 | |
---|---|
서울 | 9631482 |
부산 | 3393191 |
인천 | 2632035 |
대구 | 2431774 |
df["2010"] # type(df["2010"]) => pandas.core.series.Series
서울 9631482
부산 3393191
인천 2632035
대구 2431774
Name: 2010, dtype: int64
데이터프레임의 열 인덱스가 문자열 라벨을 가지고 있는 경우에는 순서를 나타내는 정수 인덱스를 열 인덱싱에 사용할 수 없다.
try:
df[0]
except Exception as e:
print(type(e))
<class 'KeyError'>
다만 원래부터 문자열이 아닌 정수형 열 인덱스를 가지는 경우에는 인덱스 값으로 정수를 사용할 수 있다.
import numpy as np
df2 = pd.DataFrame(np.arange(12).reshape(3, 4))
df2
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
df2[2]
0 2
1 6
2 10
Name: 2, dtype: int32
df2[[1, 2]]
1 | 2 | |
---|---|---|
0 | 1 | 2 |
1 | 5 | 6 |
2 | 9 | 10 |
행 인덱싱
만약 행 단위로 인덱싱을 하고자 하면 항상 슬라이싱(slicing)을 해야 한다. 인덱스의 값이 문자 라벨이면 라벨 슬라이싱도 가능하다.
df[:1]
지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |
---|---|---|---|---|---|---|
서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | -1.34 |
df[1:2]
지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |
---|---|---|---|---|---|---|
부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | -3.4 |
df["서울":"부산"]
지역 | 2015 | 2010 | 2005 | 2000 | 2005-2010 등가율 | |
---|---|---|---|---|---|---|
서울 | 수도권 | 9904312 | 9631482 | 9762546 | 9853972 | -1.34 |
부산 | 경상권 | 3448737 | 3393191 | 3512547 | 3655437 | -3.40 |
개별 데이터 인덱싱
데이터프레임에서 열 라벨로 시리즈를 인덱싱하면 시리즈가 된다. 이 시리즈를 다시 행 라벨로 인덱싱하면 개발 데이터가 나온다.
df["2015"]["서울"]
9904312
지금까지의 데이터프레임 인덱싱 방법을 정리하면 다음과 같다.
인덱싱값 | 가능 | 결과 | 자료형 | 추가사항 |
---|---|---|---|---|
라벨 | O | 열 | 시리즈 | |
라벨 리스트 | O | 열 | 데이터프레임 | |
인덱스데이터(정수) | X | 열 라벨이 정수인 경우 라벨 인덱싱으로 인정 | ||
인덱스데이터(정수 슬라이스) | O | 행 | 데이터프레임 |
출처 : 데이터사이언스 스쿨(datascienceschool.net)