작성자: admin 작성일시: 2016-04-15 21:13:37 조회수: 4045 다운로드: 141
카테고리: Python 태그목록:

Pandas 패키지의 소개

Pandas 패키지

Pandas 패키지는 데이터 분석을 할 때 가장 많이 쓰이는 패키지 중의 하나이다. 대부분의 데이터는 시계열(series)이나 표(table)의 형태로 나타낼 수 있는데 Pandas 패키지에서는 이러한 표 데이터를 다루기 위한 시리즈(Series) 클래스와 데이터프레임(DataFrame) 클래스를 제공한다.

시리즈 클래스

시리즈 클래스는 NumPy에서 제공하는 1차원 배열과 비슷하지만 각 데이터의 의미를 표시하는 인덱스(index)를 붙일 수 있다.

시리즈 생성

데이터를 리스트나 1차원 배열 형식으로 Series 클래스 생성자에 넣어주면 시리즈 클래스 객체를 만들 수 있다. 인덱스의 길이는 데이터의 길이와 같아야 한다. 다음 예에서 이 "서울", "부산" 등의 문자열이 인덱스의 값이다. 인덱스의 값을 인덱스 라벨(label)이라고도 한다. 인덱스 라벨은 문자열 뿐 아니라 날짜, 시간, 정수 등도 가능하다.

다음 예제는 각 도시의 2015년 인구 데이터를 시리즈로 만든 것이다.

In:
s = pd.Series([9904312, 3448737, 2890451, 2466052], index=["서울", "부산", "인천", "대구"])
s
Out:
서울    9904312
부산    3448737
인천    2890451
대구    2466052
dtype: int64

만약 인덱스를 지정하지 않고 시리즈를 만들면 시리즈의 인덱스는 0부터 시작하는 정수값이 된다.

In:
pd.Series(range(10, 14))
Out:
0    10
1    11
2    12
3    13
dtype: int64

시리즈의 인덱스는 index 속성으로 접근할 수 있다. 시리즈의 값은 사실 NumPy의 1차원 배열이며 values 속성으로 접근할 수 있다.

In:
s.index
Out:
Index([u'서울', u'부산', u'인천', u'대구'], dtype='object')
In:
s.values
Out:
array([9904312, 3448737, 2890451, 2466052])

또한 시리즈 데이터는 name 속성을 이용하여 자체적인 이름을 붙일 수 있으며 index.name 속성으로 인덱스에도 이름을 붙일 수 있다.

In:
s.name = "인구"
s.index.name = "도시"
s
Out:
도시
서울    9904312
부산    3448737
인천    2890451
대구    2466052
Name: 인구, dtype: int64

시리즈 연산

시리즈는 NumPy 배열과 동일하게 벡터화 연산을 할 수 있다. 다만 연산을 해도 인덱스 값은 변하지 않는다. 예를 들어 인수를 백만 단위로 만들기 위해 시리즈 객체 전체를 1,000,000 으로 나누어도 인덱스 라벨에는 영향을 미치지 않는 것을 볼 수 있다.

In:
s / 1000000
Out:
도시
서울    9.904312
부산    3.448737
인천    2.890451
대구    2.466052
Name: 인구, dtype: float64

시리즈 인덱싱

또한 NumPy 배열에서 가능한 인덱스 방법이외에도 인덱스 라벨을 이용한 인덱싱도 할 수 있다. 배열 인덱싱이나 인덱스 라벨을 이용한 슬라이싱(slicing)도 가능하다.

In:
s[1], s["부산"]
Out:
(3448737, 3448737)
In:
s[3], s["대구"]
Out:
(2466052, 2466052)

배열 인덱싱을 하면 자료의 순서를 바꾸거나 특정한 자료만 선택할 수 있다.

In:
s[[0, 3, 1]]
Out:
도시
서울    9904312
대구    2466052
부산    3448737
Name: 인구, dtype: int64
In:
s[["서울", "대구", "부산"]]
Out:
도시
서울    9904312
대구    2466052
부산    3448737
Name: 인구, dtype: int64
In:
s[(250e4 < s) & (s < 500e4)]
Out:
도시
부산    3448737
인천    2890451
Name: 인구, dtype: int64

문자열 라벨을 이용한 슬라이싱을 하는 경우 콜론(:) 기호 뒤에 오는 인덱스에 해당하는 값도 포함된다는 점이 일반적인 인덱싱과 다르다.

In:
s[1:3]
Out:
도시
부산    3448737
인천    2890451
Name: 인구, dtype: int64
In:
s["부산":"대구"]
Out:
도시
부산    3448737
인천    2890451
대구    2466052
Name: 인구, dtype: int64

만약 라벨 값이 영문 문자열인 경우에는 마치 속성인것 처럼 점(.)을 이용하여 접근할 수도 있다.

In:
s0 = pd.Series(range(3), index=["a", "b", "c"])
s0
Out:
a    0
b    1
c    2
dtype: int64
In:
s0.a
Out:
0
In:
s0.b
Out:
1

시리즈와 사전 자료형

시리즈 객체는 라벨 값에 의해 인덱싱이 가능하므로 실질적으로 라벨 값을 키(key)로 가지는 사전(dict) 자료형과 같다고 볼 수 있다.

따라서 사전 자료형에서 제공하는 in 연산도 가능하고 iteritems 메서드를 사용하면 for 루프를 통해 각 원소의 키(key)와 값(value)을 접근할 수도 있다.

In:
"서울" in s
Out:
True
In:
"대전" in s
Out:
False
In:
for k, v in s.iteritems():
    print("%s = %d" % (k, v))
서울 = 9904312
부산 = 3448737
인천 = 2890451
대구 = 2466052

또 사전 객체에서 시리즈를 만들 수도 있다. 이번에는 2010년의 인구 자료를 시리즈로 만들어 보자.

In:
s2 = pd.Series({"서울": 9631482, "부산": 3393191, "인천": 2632035, "대전": 1490158})
s2
Out:
대전    1490158
부산    3393191
서울    9631482
인천    2632035
dtype: int64

사전의 원소는 순서를 가지지 않으므로 시리즈의 데이터도 순서가 보장되지 않는다. 만약 순서를 정하고 싶다면 인덱스를 리스트로 지정해야 한다.

In:
s2 = pd.Series({"서울": 9631482, "부산": 3393191, "인천": 2632035, "대전": 1490158}, 
               index=["부산", "서울", "인천", "대전"])
s2
Out:
부산    3393191
서울    9631482
인천    2632035
대전    1490158
dtype: int64

인덱스 기반 연산

이번에는 2015년도와 2010년의 인구 증가를 계산해 보자. 두 개의 시리즈의 차이를 구하면 된다. 두 시리즈에 대해 연산을 하는 경우 인덱스가 같은 데이터끼지 알아서 차이를 구해 준다. 즉 인덱스 기반으로 연산을 한다.

In:
ds = s - s2
ds
Out:
대구         NaN
대전         NaN
부산     55546.0
서울    272830.0
인천    258416.0
dtype: float64

대구와 대전의 경우에는 2010년 자료와 2015년 자료가 모두 존재하지 않기 때문에 계산이 불가능하므로 NaN(Not a Number)이라는 값을 가지게 된다. 또한 NaN 값이 float 자료형에서만 가능하므로 다른 계산 결과도 모두 float 자료형이 되었다는 점에 주의한다. NaN이 아닌 값을 구하려면 notnull 메서드를 사용한다.

In:
ds.notnull()
Out:
대구    False
대전    False
부산     True
서울     True
인천     True
dtype: bool
In:
ds[ds.notnull()]
Out:
부산     55546.0
서울    272830.0
인천    258416.0
dtype: float64

마찬가지로 인구 증가율(%)은 다음과 같이 구할 수 있다.

In:
rs = (s - s2) / s2 * 100
rs = rs[rs.notnull()]
rs
Out:
부산    1.636984
서울    2.832690
인천    9.818107
dtype: float64

데이터의 갱신, 추가, 삭제

인덱싱을 이용하면 사전처럼 데이터를 갱신(update)하거나 추가(add)할 수 있다.

In:
rs["부산"] = 1.63
rs
Out:
부산    1.630000
서울    2.832690
인천    9.818107
dtype: float64
In:
rs["대구"] = 1.41
rs
Out:
부산    1.630000
서울    2.832690
인천    9.818107
대구    1.410000
dtype: float64

삭제시에도 사전처럼 del 명령을 사용한다.

In:
del rs["서울"]
rs
Out:
부산    1.630000
인천    9.818107
대구    1.410000
dtype: float64

연습 문제 1

  1. 원하는 자료를 기반으로 두 개의 시리즈 객체를 만들어 본다. 문자열 인덱스를 가져야 하며 두 시리즈에 공통적으로 포함되지 않는 라벨이 있어야 한다.
  2. 두 시리즈 객체를 이용하여 연산을 한다.

데이터프레임 클래스

시리즈가 1차원 벡터 데이터에 행방향 인덱스(row index)를 붙인 것이라면 데이터프레임 클래스는 2차원 행렬 데이터에 인덱스를 붙인 것과 비슷하다. 2차원이므로 행방향 인덱스(row index) 뿐 아니라 열방향 인덱스(column index)도 붙일 수 있다.

데이터프레임 생성

데이터프레임을 만드는 방법은 다양하다. 가장 간단한 방법은 다음과 같이 리스트나 일차원 배열을 값(value)으로 가지고 열방향 인덱스 라벨을 키(key)로 가지는 사전(dictionary) 데이터를 DataFrame 클래스 생성자에 넣는다. 이 때 열방향 인덱스는 columns 인수에, 행방향 인덱스는 index 인수에 지정한다.

In:
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
Out:
지역 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)를 사전(dictionary)처럼 묶어놓은 것이라고 보는 것이 더 정확하다. 2차원 배열 데이터는 모든 원소가 같은 자료형을 가져야 하지만 데이터프레임은 각 열(column)마다 자료형이 다를 수 있기 때문이다. 위 예제에서도 지역과 인구와 증가율은 각각 문자열, 정수, 부동소수점 실수이다.

시리즈와 마찬가지로 데이터만 접근하려면 values 속성을 사용한다. 열방향 인덱스와 행방향 인덱스는 각각 columns, index 속성으로 접근한다.

In:
df.values
Out:
array([['\xec\x88\x98\xeb\x8f\x84\xea\xb6\x8c', 9904312, 9631482, 9762546,
        9853972, 0.0283],
       ['\xea\xb2\xbd\xec\x83\x81\xea\xb6\x8c', 3448737, 3393191, 3512547,
        3655437, 0.0163],
       ['\xec\x88\x98\xeb\x8f\x84\xea\xb6\x8c', 2890451, 2632035, 2517680,
        2466338, 0.0982],
       ['\xea\xb2\xbd\xec\x83\x81\xea\xb6\x8c', 2466052, 2431774, 2456016,
        2473990, 0.0141]], dtype=object)
In:
df.columns
Out:
Index([u'지역', u'2015', u'2010', u'2005', u'2000', u'2010-2015 증가율'], dtype='object')
In:
df.index
Out:
Index([u'서울', u'부산', u'인천', u'대구'], dtype='object')

시리즈에서 처럼 열방향 인덱스와 행방향 인덱스에 이름을 붙이는 것도 가능하다.

In:
df.index.name = "도시"
df.columns.name = "특성"
df
Out:
특성 지역 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

데이터프레임 열 인덱싱

따라서 데이터프레임을 인덱싱을 할 때도 열 라벨(column label)을 키값으로 생각하여 인덱싱을 할 수 있다. 인덱스로 라벨 값을 하나만 넣으면 시리즈 객체가 반환되고 라벨의 배열 또는 리스트를 넣으면 부분적인 데이터프레임이 반환된다.

In:
df["지역"]
Out:
도시
서울    수도권
부산    경상권
인천    수도권
대구    경상권
Name: 지역, dtype: object
In:
df[["2010", "2015"]]
Out:
특성 2010 2015
도시
서울 9631482 9904312
부산 3393191 3448737
인천 2632035 2890451
대구 2431774 2466052

만약 하나의 열만 빼내면서 데이터프레임 자료형을 유지하고 싶다면 원소가 하나인 리스트를 써서 인덱싱하면 된다.

In:
df[["2010"]]
Out:
특성 2010
도시
서울 9631482
부산 3393191
인천 2632035
대구 2431774
In:
type(df[["2010"]])
Out:
pandas.core.frame.DataFrame
In:
df["2010"]
Out:
도시
서울    9631482
부산    3393191
인천    2632035
대구    2431774
Name: 2010, dtype: int64
In:
type(df["2010"])
Out:
pandas.core.series.Series

데이터프레임의 열 인덱스가 문자열 라벨을 가지고 있는 경우에는 순서를 나타내는 정수 인덱스를 열 인덱싱에 사용할 수 없다. 정수 인덱싱의 슬라이스는 뒤에서 설명하겠지만 행(row)을 인덱싱할 때 사용하므로 열을 인덱싱할 때는 쓸 수 없다.

In:
df[0]

KeyErrorTraceback (most recent call last)
 in ()
----> 1 df[0]

/home/dockeruser/anaconda2/lib/python2.7/site-packages/pandas/core/frame.pyc in __getitem__(self, key)
   2057             return self._getitem_multilevel(key)
   2058         else:
-> 2059             return self._getitem_column(key)
   2060 
   2061     def _getitem_column(self, key):

/home/dockeruser/anaconda2/lib/python2.7/site-packages/pandas/core/frame.pyc in _getitem_column(self, key)
   2064         # get column
   2065         if self.columns.is_unique:
-> 2066             return self._get_item_cache(key)
   2067 
   2068         # duplicate columns & possible reduce dimensionality

/home/dockeruser/anaconda2/lib/python2.7/site-packages/pandas/core/generic.pyc in _get_item_cache(self, item)
   1384         res = cache.get(item)
   1385         if res is None:
-> 1386             values = self._data.get(item)
   1387             res = self._box_item_values(item, values)
   1388             cache[item] = res

/home/dockeruser/anaconda2/lib/python2.7/site-packages/pandas/core/internals.pyc in get(self, item, fastpath)
   3541 
   3542             if not isnull(item):
-> 3543                 loc = self.items.get_loc(item)
   3544             else:
   3545                 indexer = np.arange(len(self.items))[isnull(self.items)]

/home/dockeruser/anaconda2/lib/python2.7/site-packages/pandas/indexes/base.pyc in get_loc(self, key, method, tolerance)
   2134                 return self._engine.get_loc(key)
   2135             except KeyError:
-> 2136                 return self._engine.get_loc(self._maybe_cast_indexer(key))
   2137 
   2138         indexer = self.get_indexer([key], method=method, tolerance=tolerance)

pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:4433)()

pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:4279)()

pandas/src/hashtable_class_helper.pxi in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:13742)()

pandas/src/hashtable_class_helper.pxi in pandas.hashtable.PyObjectHashTable.get_item (pandas/hashtable.c:13696)()

KeyError: 0

다만 원래부터 문자열로 된 열 라벨이 주어지지 않아서 정수형 열 인덱스가 만들어진 경우에는 인덱스 정수를 사용할 수 있다.

In:
df2 = pd.DataFrame(np.arange(12).reshape(3, 4))
df2
Out:
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
In:
df2[2]
Out:
0     2
1     6
2    10
Name: 2, dtype: int64

열 데이터의 갱신, 추가, 삭제

데이터프레임은 열 시리즈의 사전으로 볼 수 있으므로 열 단위로 데이터를 갱신하거나 추가, 삭제할 수 있다.

In:
df["2010-2015 증가율"] = df["2010-2015 증가율"] * 100
df
Out:
특성 지역 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
In:
df["2005-2010 증가율"] = ((df["2010"] - df["2005"]) / df["2005"] * 100).round(2)
df
Out:
특성 지역 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
In:
del df["2010-2015 증가율"]
df
Out:
특성 지역 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

개별 데이터 인덱싱

데이터프레임에서 열 라벨로 시리즈를 인덱싱하고 다시 행 라벨로 개별 데이터를 인덱싱할 수 있다.

In:
df["2015"]["서울"]
Out:
9904312

행 단위 인덱싱

만약 행 단위로 인덱싱을 하고자 하면 항상 슬라이싱(slicing)을 해야 한다. 시리즈와 같이 라벨 슬라이싱도 가능하다.

In:
df[:1]
Out:
특성 지역 2015 2010 2005 2000 2005-2010 증가율
도시
서울 수도권 9904312 9631482 9762546 9853972 -1.34
In:
df[1:2]
Out:
특성 지역 2015 2010 2005 2000 2005-2010 증가율
도시
부산 경상권 3448737 3393191 3512547 3655437 -3.4
In:
df[1:3]
Out:
특성 지역 2015 2010 2005 2000 2005-2010 증가율
도시
부산 경상권 3448737 3393191 3512547 3655437 -3.40
인천 수도권 2890451 2632035 2517680 2466338 4.54
In:
df["서울":"부산"]
Out:
특성 지역 2015 2010 2005 2000 2005-2010 증가율
도시
서울 수도권 9904312 9631482 9762546 9853972 -1.34
부산 경상권 3448737 3393191 3512547 3655437 -3.40

연습 문제 2

각자 원하는 데이터로 데이터프레임을 만든다. 단 다음 조건을 만족해야 한다.

  1. 열의 갯수와 행의 갯수가 각각 5개 이상이어야 한다.
  2. 열에는 정수, 문자열, 실수 자료형 데이터가 각각 1개 이상씩 포함되어 있어야 한다.

질문/덧글

PC에 csv, 엑셀파일 등의 업로드는 어떻게 하나요? minj*** 2016년 6월 23일 10:00 오후

예시에는 이제 패키지에 저장되어 있는 샘플데이를 사용하지만, 만약 다른곳에서 받은 엑셀 또는 csv 파일을 업로드해서 작업을 해야할 때도 있을것 같은데
어떻게 하면되는지 궁금합니다!

답변: PC에 csv, 엑셀파일 등의 업로드는 어떻게 하나요? 관리자 2016년 6월 23일 11:43 오후

주피터 노트북에서 우측 상단에 있는 "Upload" 버튼을 사용하세요. 파일 포맷에 상관없이 가능합니다.

패키지 다운로드가 사라졌습니다. junt*** 2017년 6월 8일 1:05 오전

docker가 오류가 생겨 재설치를 했는데, 패키지를 인스톨 하려고 찾아왔는데 인스톨 명령어가 사라졌습니다.

답변: 패키지 다운로드가 사라졌습니다. 관리자 2017년 6월 8일 1:15 오전

인스톨 명령어라는게 뭔가요?