Pandas 튜토리얼

# esc + a 위에 셀추가 // esc + b 아래에 셀 추가 // esc + m 셀 마크다운 처리 // esc + y 셀 코드 처리 // esc + dd 셀 삭제

판다스(Pandas) 튜토리얼

판다스(Pandas)는 파이썬에서 데이터분석을 위해 사용하는 패키지(Package) 중 하나입니다. 간단히 요약하자면, “파이썬으로 엑셀과 같은 파일을 다루기에 최적화된 도구”라고 생각하시면 됩니다.

판다스는 엑셀과 비슷하지만 다음과 같은 장점을 갖고 있습니다.

  1. 대용량 데이터를 효율적으로 다룰 수 있습니다. 엑셀은 데이터의 용량이 100MB만 넘어가면 정상적으로 작동하지 않는데, 판다스는 최소 1GB, 많으면 100GB가 넘는 데이터도 빠른 속도로 처리할 수 있습니다.
  2. 복잡한 기능을 구현할 때는 엑셀보다 더 쉽습니다. 엑셀은 복잡한 기능을 엑셀 전용 함수를 사용하는데, 함수의 문법도 직관적이지 않고 동작도 제대로 안 되는 경우가 많습니다. 반면, 판다스는 파이썬을 기반으로 동작하기 때문에 엑셀보다 더 쉽습니다.
  3. 다른 시스템과 연동하기 쉽습니다. 엑셀은 분석 결과를 데이터베이스에 집어넣거나 웹 페이지에 띄우는 일을 하기 어렵지만, 판다스는 플라스크(Flask, 웹 프레임워크)나 SQL알케미(SQL Alchemy, 데이터베이스)를 통해 다른 시스템과 연동하기 매우 편합니다.

판다스는 파이썬과 달리 일반인의 직관과 상식과 다소 동떨어진 방식으로 동작합니다. 하지만 판다스의 기본적인 룰 몇 가지만 숙지하면, 적은 문법 만으로도 굉장히 다양하고 창의적인 방식으로 엑셀(또는 CSV) 파일을 다룰 수 있습니다.

다음의 튜토리얼에 익숙해진 뒤, 10 Minutes to pandas라는 판다스의 공식 튜토리얼을 읽어보시는 것을 추천 드립니다. 또는 판다스를 속성으로 배우고 싶거나, 책에서 나오지 않는 실전 노하우(한글 데이터 처리, 데이터베이스 연동 등)를 배우고 싶다면. DS School의 중급(판다스) 과정을 수강하는 것을 추천 드립니다.

패키지 읽어오기

파이썬은 범용적인 목적으로 만들어진 프로그래밍 언어입니다. 하지만 범용적인 프로그래밍 언어라 하더라도,세상의 모든 기능을 전부 구현하는 것은 이론적으로 불가능합니다.

그래서 파이썬은 패키지(package)라는 시스템을 만들었습니다. 이제 개발자는 파이썬에 존재하지 않는 기능이 있다면 이를 스스로 구현한 뒤 세상에 공개할 수 있고, 파이썬은 패키지라는 시스템을 통해 이 기능을 파이썬에서 쉽게 사용할 수 있도록 구현해 놓았습니다.

다음은 판다스라는 패키지를 파이썬에서 읽어오는 코드입니다.

# pandas 라는 패키지를 파이썬으로 읽어(import)옵니다. 그리고 pd라는 축약어로 사용합니다.
import pandas as pd

파일 읽어오기

판다스에서는 read_csv라는 기능으로 CSV(comma-separated values)파일을 읽어올 수 있습니다. 경로를 읽어올 때는 컴퓨터에 있는 파일을 절대경로와 상대경로로 읽어올 수도 있고, 아니면 주소(URL)에서 바로 데이터를 읽어올 수도 있습니다.

# 판다스 실습을 위한 데이터가 저장된 원격 경로를 지정합니다.
file_url = "http://bit.ly/ds01-weight-history"

# 위에서 지정한 경로를, 판다스의 read_csv 함수를 활용해 데이터를 읽어옵니다.
# 읽어올 때 '회차' 컬럼을 판다스의 인덱스(Index)로 지정합니다.
# 그리고 이 결과를 data라는 이름의 변수에 할당합니다.
data = pd.read_csv(file_url, index_col = "회차")

# data 컬럼을 출력해서 확인합니다.
data
이름 측정일 몸무게 단위 담당 지점
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남
2 강성희 2019-01-02 75.7 kg 김지수 강남
3 강성희 2019-01-03 76.0 kg 김지수 강남
4 강성희 2019-01-04 NaN kg 최재우 여의도
5 강성희 2019-01-05 76.2 kg 김지수 강남구
6 강성희 2019-01-06 75.7 kg 최재우 여의도
7 강성희 2019-01-07 NaN kg 최재우 여의도
8 강성희 2019-01-08 NaN kg 김민철 여의도
9 강성희 2019-01-09 75.0 kg 김민철 여의도

여기서 가져온 결과를 판다스의 전문 용어로 데이터프레임(DataFrame) 이라고 합니다.

데이터프레임을 읽어왔으면 여기서 인덱스(index)와 컬럼(columns)을 가져올 수 있습니다.

# 인덱스(index)를 가져옵니다. 여기서 index는 '회차'와 동일합니다.
data.index
Int64Index([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64', name='회차')
# 컬럼들(columns)을 가져옵니다.
data.columns
Index(['이름', '측정일', '몸무게', '단위', '담당', '지점'], dtype='object')

또한 head로 상위 5개를, tail으로 하위 5개의 데이터를 가져올 수 있습니다. 여기서 headtail안에 숫자를 넣어주면 숫자 만큼의 데이터를 가져옵니다. (가령 .head(3)은 상위 5개가 아닌 상위 3개의 데이터를 가져옵니다.)

# 데이터의 상위 5개를 출력합니다.
data.head()
이름 측정일 몸무게 단위 담당 지점
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남
2 강성희 2019-01-02 75.7 kg 김지수 강남
3 강성희 2019-01-03 76.0 kg 김지수 강남
4 강성희 2019-01-04 NaN kg 최재우 여의도
5 강성희 2019-01-05 76.2 kg 김지수 강남구
# head에 갯수(n)를 지정해주면 n개 만큼의 데이터를 가져옵니다,
# 아래 코드는 데이터의 상위 3개를 출력합니다.
data.head(3)
이름 측정일 몸무게 단위 담당 지점
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남
2 강성희 2019-01-02 75.7 kg 김지수 강남
3 강성희 2019-01-03 76.0 kg 김지수 강남
# 데이터의 하위 5개를 출력합니다.
data.tail()
이름 측정일 몸무게 단위 담당 지점
회차
5 강성희 2019-01-05 76.2 kg 김지수 강남구
6 강성희 2019-01-06 75.7 kg 최재우 여의도
7 강성희 2019-01-07 NaN kg 최재우 여의도
8 강성희 2019-01-08 NaN kg 김민철 여의도
9 강성희 2019-01-09 75.0 kg 김민철 여의도
# head(n)과 동일한 원리로, tail(n)은 하위 n개의 데이터를 가져옵니다.
# 아래 코드는 데이터의 하위 7개를 출력합니다.
data.tail(7)
이름 측정일 몸무게 단위 담당 지점
회차
3 강성희 2019-01-03 76.0 kg 김지수 강남
4 강성희 2019-01-04 NaN kg 최재우 여의도
5 강성희 2019-01-05 76.2 kg 김지수 강남구
6 강성희 2019-01-06 75.7 kg 최재우 여의도
7 강성희 2019-01-07 NaN kg 최재우 여의도
8 강성희 2019-01-08 NaN kg 김민철 여의도
9 강성희 2019-01-09 75.0 kg 김민철 여의도

기본 기능

처음에 배울 기능은 판다스의 기본으로 내장되어있는 기능입니다. 판다스에서는 간단한 수학/통계 연산(ex: 평균, 합 etc)을 포함한 몇몇 기능이 기본으로 탑재되어 있는데, 이 기능을 미리 숙지하고 있으면 차후에 큰 도움이 됩니다.

# .mean() 은 평균을 계산하는 기능입니다.
# 아래 코드는 몸무게의 평균(약 75.8 kg)을 계산 후 출력합니다.
data["몸무게"].mean()
75.83333333333333
# .min() 는 최소치를 계산하는 기능입니다.
# 아래 코드는 몸무게가 가장 적었을 때의 몸무게(75.0 kg)를 계산 후 출력합니다.
data["몸무게"].min()
75.0
# .min() 는 최대치를 계산하는 기능입니다.
# 아래 코드는 몸무게가 가장 많았을 때의 몸무게(76.4 kg)를 계산 후 출력합니다.
data["몸무게"].max()
76.4

또는 .describe()를 통해 전체 숫자 컬럼(정수형, 소수점)의 통계치를 한 눈에 볼 수 있습니다.

# 통계를 낼 수 있는 숫자 데이터(ex: 몸무게)에 한해 주요 통계치를 보여줍니다.
data.describe()
몸무게
count 6.000000
mean 75.833333
std 0.492612
min 75.000000
25% 75.700000
50% 75.850000
75% 76.150000
max 76.400000

만일 특정 컬럼의 값을 수정하고 싶다면 replace를 사용하면 됩니다.

# '강남구'라고 적힌 값을 '강남'으로 통일해주기 위해 replace를 사용합니다. 
# 이를 '지점' 컬럼에 다시 대입하여 수정합니다.
data["지점"] = data["지점"].replace("강남구", "강남")

# 이제 '강남구'가 '강남'으로 바꿘 것을 확인할 수 있습니다.
data
이름 측정일 몸무게 단위 담당 지점
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남
2 강성희 2019-01-02 75.7 kg 김지수 강남
3 강성희 2019-01-03 76.0 kg 김지수 강남
4 강성희 2019-01-04 NaN kg 최재우 여의도
5 강성희 2019-01-05 76.2 kg 김지수 강남
6 강성희 2019-01-06 75.7 kg 최재우 여의도
7 강성희 2019-01-07 NaN kg 최재우 여의도
8 강성희 2019-01-08 NaN kg 김민철 여의도
9 강성희 2019-01-09 75.0 kg 김민철 여의도

데이터를 보면 비어있는(NaN) 값이 있는데, 이런 값을 채워주고 싶다면 fillna라는 기능을 사용할 수 있습니다.

# 몸무게가 비어있는(NaN) 데이터에 한해서 75.8 kg로 채워줍니다.
# 이를 '몸무게(fill)'이라는 새로운 컬럼에 추가해줍니다.
data['몸무게'] = data['몸무게'].fillna(75.8)

# 이제 NaN값이 75.8로 바뀐 것을 알 수 있습니다.
data
이름 측정일 몸무게 단위 담당 지점
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남
2 강성희 2019-01-02 75.7 kg 김지수 강남
3 강성희 2019-01-03 76.0 kg 김지수 강남
4 강성희 2019-01-04 75.8 kg 최재우 여의도
5 강성희 2019-01-05 76.2 kg 김지수 강남구
6 강성희 2019-01-06 75.7 kg 최재우 여의도
7 강성희 2019-01-07 75.8 kg 최재우 여의도
8 강성희 2019-01-08 75.8 kg 김민철 여의도
9 강성희 2019-01-09 75.0 kg 김민철 여의도

또 다른 재미있는 기능은 .shift() 입니다. 이 기능은 전체 데이터를 기준으로 앞 행(row)의 데이터와 뒤 행(row)의 데이터를 가져옵니다. 앞 행의 데이터는 .shift() 또는 .shift(+1)으로, 뒤 행의 데이터는 .shift(-1)으로 가져올 수 있습니다.

# .shift(+1)으로 이전 날짜의 몸무게를 가져옵니다.
# 혹시나 이전 기록이 없다면(=첫 번째 데이터) NaN을 가져옵니다.
data['몸무게(fill)(prev)'] = data['몸무게(fill)'].shift(+1)

# .shift(-1)으로 다음 날짜의 몸무게를 가져옵니다.
# 혹시나 다음 기록이 없다면(=마지막 데이터) NaN을 가져옵니다.
data['몸무게(fill)(next)'] = data['몸무게(fill)'].shift(-1)

# 이전 몸무게와 다음 몸무게가 추가된 것을 확인할 수 있습니다.
data
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
4 강성희 2019-01-04 NaN kg 최재우 여의도 75.8 76.0 76.2
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8
8 강성희 2019-01-08 NaN kg 김민철 여의도 75.8 75.8 75.0
9 강성희 2019-01-09 75.0 kg 김민철 여의도 75.0 75.8 NaN

만일 날짜 데이터가 있을 경우에는 판다스의 to_datetime을 활용해서 연/월/일을 쉽게 가져올 수 있습니다.

# 판다스의 to_datetime를 사용합니다.
# 이 기능을 사용하면 이제 연/월/일을 쉽게 가져올 수 있습니다.
data['측정일'] = pd.to_datetime(data['측정일'])

# 연도(year) 데이터를 가져오고 싶으면 dt.year를 사용합니다.
data['측정일'].dt.year
회차
1    2019
2    2019
3    2019
4    2019
5    2019
6    2019
7    2019
8    2019
9    2019
Name: 측정일, dtype: int64
# 월(month) 데이터를 가져오고 싶으면 dt.month를 사용합니다.
data['측정일'].dt.month
회차
1    1
2    1
3    1
4    1
5    1
6    1
7    1
8    1
9    1
Name: 측정일, dtype: int64
# 일(day) 데이터를 가져오고 싶으면 dt.day를 사용합니다.
data['측정일'].dt.day
회차
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
Name: 측정일, dtype: int64

행렬

판다스 데이터프레임의 기본은 행렬 접근하기입니다. (참고로 행은 row, 열은 column이라고 합니다) 행렬 접근을 시도해보면 판다스의 특징을 바로 깨달을 수 있는데, 일단 1) 굉장히 간단한 문법으로 행렬을 접근할 수 있고, 2) 이 문법을 조금만 응용하면 다양한 방식으로 행렬을 가져올 수 있습니다.

먼저 열(column)을 가져오는 방법부터 배워보겠습니다.

열(column) 가져오기

일단 대괄호([])를 엽니다. 이 대괄호에 문자열을 집어넣으면, 문자열에 해당하는 열(column)을 반환합니다.

# '몸무게'라는 이름의 컬럼을 가져옵니다.
data["몸무게"]
회차
1    76.4
2    75.7
3    76.0
4     NaN
5    76.2
6    75.7
7     NaN
8     NaN
9    75.0
Name: 몸무게, dtype: float64

컬럼 가져오기의 응용. 대괄호를 두 개([[]]) 연 뒤, 컬럼명 여러개를 집어넣습니다.

# '이름', '측정일', '몸무게' 컬럼 세 개를 가져옵니다.
data[["이름", "측정일", "몸무게"]]
이름 측정일 몸무게
회차
1 강성희 2019-01-01 76.4
2 강성희 2019-01-02 75.7
3 강성희 2019-01-03 76.0
4 강성희 2019-01-04 NaN
5 강성희 2019-01-05 76.2
6 강성희 2019-01-06 75.7
7 강성희 2019-01-07 NaN
8 강성희 2019-01-08 NaN
9 강성희 2019-01-09 75.0

위의 컬럼 여러개 가져오기는 파이썬의 리스트(list)를 응용한 것입니다. 위의 코드와 아래의 코드는 사실상 동일합니다.

# 리스트를 하나 만드는데, 리스트 안에 컬럼 이름 여러개('이름', '측정 일자', '몸무게')를 집어넣습니다.
# 이후 판다스 데이터프레임에서 대괄호를 하나 열고([]) 그 안에 리스트를 집어넣습니다.
columns = ["이름", "측정일", "몸무게"]

data[columns]
이름 측정일 몸무게
회차
1 강성희 2019-01-01 76.4
2 강성희 2019-01-02 75.7
3 강성희 2019-01-03 76.0
4 강성희 2019-01-04 NaN
5 강성희 2019-01-05 76.2
6 강성희 2019-01-06 75.7
7 강성희 2019-01-07 NaN
8 강성희 2019-01-08 NaN
9 강성희 2019-01-09 75.0

컬럼을 가져올 때 주의해야 할 것이 있습니다. ‘회차’는 이제 컬럼이 아닌 인덱스이기 때문에, 컬럼 검색으로는 가져올 수 없습니다. 이 점 주의하셔야 합니다.

# '회차'는 컬럼이 아닌 인덱스이기 때문에, []로 가져오는 것이 불가능합니다.
data["회차"]
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   3077             try:
-> 3078                 return self._engine.get_loc(key)
   3079             except KeyError:


pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()


pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()


pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()


pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()


KeyError: '회차'


During handling of the above exception, another exception occurred:


KeyError                                  Traceback (most recent call last)

<ipython-input-234-f8fa0b56fe04> in <module>
      1 # '회차'는 컬럼이 아닌 인덱스이기 때문에, []로 가져오는 것이 불가능합니다.
----> 2 data["회차"]


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/frame.py in __getitem__(self, key)
   2686             return self._getitem_multilevel(key)
   2687         else:
-> 2688             return self._getitem_column(key)
   2689 
   2690     def _getitem_column(self, key):


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/frame.py in _getitem_column(self, key)
   2693         # get column
   2694         if self.columns.is_unique:
-> 2695             return self._get_item_cache(key)
   2696 
   2697         # duplicate columns & possible reduce dimensionality


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/generic.py in _get_item_cache(self, item)
   2487         res = cache.get(item)
   2488         if res is None:
-> 2489             values = self._data.get(item)
   2490             res = self._box_item_values(item, values)
   2491             cache[item] = res


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/internals.py in get(self, item, fastpath)
   4113 
   4114             if not isna(item):
-> 4115                 loc = self.items.get_loc(item)
   4116             else:
   4117                 indexer = np.arange(len(self.items))[isna(self.items)]


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
   3078                 return self._engine.get_loc(key)
   3079             except KeyError:
-> 3080                 return self._engine.get_loc(self._maybe_cast_indexer(key))
   3081 
   3082         indexer = self.get_indexer([key], method=method, tolerance=tolerance)


pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()


pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()


pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()


pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()


KeyError: '회차'

행(row) 가져오기

열(column) 가져오기와 비슷하나, 이번에는 .loc(locate의 약자)라는 표현을 사용합니다.

# loc(locate)를 사용하면 이번에는 열(column)이 아니라 행(row)을 가져옵니다.
# 다음의 코드는 '회차'가 1회차인 승객 정보를 반환합니다.
data.loc[1]
이름                                 강성희
측정일                2019-01-01 00:00:00
몸무게                               76.4
단위                                  kg
담당                                 김지수
지점                                  강남
몸무게(fill)                         76.4
몸무게(fill)(prev)                    NaN
몸무게(fill)(next)                   75.7
Name: 1, dtype: object

파이썬의 슬라이싱과 비슷한 기능도 있습니다.

# 아래 코드는 '회차'가 1회차인 기록부터 7회차인 기록까지 반환합니다.
# 파이썬의 슬라이싱(ex: odd[0:4])과 다르게, 이번에는 '회차'가 7회차인 기록을 포함하여 출력합니다.
data.loc[1:7]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
4 강성희 2019-01-04 NaN kg 최재우 여의도 75.8 76.0 76.2
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8

행 가져오기의 응용. 이번에는 .reindex 후 대괄호를 두 개([[]]) 열고, 1, 3, 7을 넣습니다.

# '회차'가 1, 3, 7회차인 기록을 반환합니다.
data.reindex([1, 3, 7])
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8

위의 코드 역시 파이썬의 리스트(list)를 응용한 것입니다. 위의 코드와 아래의 코드는 사실상 동일합니다.

session_ids = [1, 3, 7]
data.reindex(session_ids)
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8

행렬 동시에 가져오기

위의 방법을 응용하면 행(row)과 열(column)을 동시에 접근할 수 있습니다. 행렬 가져오기는 판다스의 가장 중요한 기능 중 하나이며, 이 기능을 창의적으로 응용하면 많은 일을 한 두줄의 코드로 쉽게 해결할 수 있기 때문에 반드시 숙지할 것을 권장 드립니다.

# 행렬 가져오기의 기본. .loc 후 콤마(,)를 기준으로
# 좌측에는 행(row)을 가져오는 조건, 우측에는 열(column)을 가져오는 조건을 넣습니다.
# 아래 코드는 '회차'가 1회차인 기록의 이름을 가져옵니다.
data.loc[1, "이름"]
'강성희'
# 여러 열을 가져오기. '회차'가 1회차인 기록의 '이름', '측정일', '몸무게'를 가져옵니다.
data.loc[1, ["이름", "측정일", "몸무게"]]
이름                     강성희
측정일    2019-01-01 00:00:00
몸무게                   76.4
Name: 1, dtype: object
# 여러 행을 가져오기. '회차'가 1, 3, 7회차인 기록의 '이름'을 가져옵니다.
data.loc[[1, 3, 7], "이름"]
회차
1    강성희
3    강성희
7    강성희
Name: 이름, dtype: object
# 여러 행을 슬라이싱해서 가져오기. '회차'가 1회차부터 7회차까지인 기록의 이름을 가져옵니다.
data.loc[1:7, "이름"]
회차
1    강성희
2    강성희
3    강성희
4    강성희
5    강성희
6    강성희
7    강성희
Name: 이름, dtype: object
# 여러 행과 여러 열을 동시에 가져오기
# '회차'가 1, 3, 7회차인 기록의 '이름', '측정일', '몸무게'를 가져옵니다.
data.loc[[1, 3, 7], ["이름", "측정일", "몸무게"]]
이름 측정일 몸무게
회차
1 강성희 2019-01-01 76.4
3 강성희 2019-01-03 76.0
7 강성희 2019-01-07 NaN

색인

다음으로 배울 기능은 판다스의 색인(Indexing)입니다. 색인은 판다스의 꽃이라고 불리울 수 있는 강력한 기능인데, 위의 컬럼 접근하는 기능을 약간만 응용하면 매우 쉬운 방식으로 데이터를 색인할 수 있습니다.

# 담당 선생님이 김지수인 데이터를 가져옵니다.
# data["담당"] == "김지수"은 담당 선생님이 김지수이면 True, 아니면 False로 이루어진 리스트 값입니다.
# 이 리스트를 data[] 의 괄호 안에 넣어주면 값이 True인 데이터만 가져오고 False인 데이터는 버리는데
# 이게 판다스 색인의 원리입니다.
data[data["담당"] == "김지수"]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7

위 색인의 원리를 응용하면 정말 쉬운 방식으로 다양한 값을 찾을 수 있습니다.

# 몸무게가 75.0 kg보다 높은(초과) 데이터를 가져옵니다.
data[data["몸무게"] > 75]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8

판다스 색인을 할 때는 데이터의 타입을 명확하게 지정해줘야 합니다. 앞서 파이썬에서 1(정수형 1)과 “1”(문자열 1)은 다르듯이, 판다스 색인에서도 정수형과 문자열을 명확히 구분해서 사용해줘야 합니다.

# 몸무게가 75.0 kg보다 높은(초과) 데이터를 가져옵니다.
# 여기서 객실 몸무게는 정수형인데, "75"은 문자열이므로
# "다른 형식의 데이터 타입으로 비교하였다" (invalid type comparison) 는 에러가 나면서 실행이 중단됩니다.
data[data["몸무게"] > "75"]
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-246-b6933a26edf2> in <module>
      2 # 여기서 객실 몸무게는 정수형인데, "75"은 문자열이므로
      3 # "다른 형식의 데이터 타입으로 비교하였다" (invalid type comparison) 는 에러가 나면서 실행이 중단됩니다.
----> 4 data[data["몸무게"] > "75"]


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in wrapper(self, other, axis)
   1281 
   1282             with np.errstate(all='ignore'):
-> 1283                 res = na_op(values, other)
   1284             if is_scalar(res):
   1285                 raise TypeError('Could not compare {typ} type with Series'


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in na_op(x, y)
   1167                     result = method(y)
   1168                 if result is NotImplemented:
-> 1169                     raise TypeError("invalid type comparison")
   1170             else:
   1171                 result = op(x, y)


TypeError: invalid type comparison
# 단위이 kg인 데이터만 가져옵니다.
data[data["단위"] == "kg"]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
4 강성희 2019-01-04 NaN kg 최재우 여의도 75.8 76.0 76.2
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8
8 강성희 2019-01-08 NaN kg 김민철 여의도 75.8 75.8 75.0
9 강성희 2019-01-09 75.0 kg 김민철 여의도 75.0 75.8 NaN
# 단, 여기서 판다스(그리고 파이썬)은 대소문자 구분이 굉장히 민감하기 때문에 이를 정확하게 맞춰줘야 합니다.
# 즉, kg가 아닌 Kg로 검색하면 대소문자가 맞지 않기 때문에 아무 데이터도 나오지 않습니다.
data[data["단위"] == "Kg"]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차

이제부터는 몇 가지 기능을 추가해서 더 다양한 방식으로 데이터를 색인해보도록 하겠습니다.

# isin() 함수를 사용합니다. 이 함수에는 리스트 값이 들어가는데,
# 리스트 안에 있는 값들 중 하나라도 있으면 True, 아니면 False를 반환합니다.

# isin을 응용하면 담당 선생님 어려 명을 한 번에 검색할 수 있습니다.
data[data["담당"].isin(["최재우", "신민철"])]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
4 강성희 2019-01-04 NaN kg 최재우 여의도 75.8 76.0 76.2
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8

판다스 색인의 경우 소괄호(())와 대괄호([])의 조합이 복잡하기 때문에 개발자가 실수할 여지가 많습니다. 그러므로 색인 코드를 작성할 때는 아래처럼 다소 코드가 길더라도 한 줄에 하나의 기능만 실행하는 것을 권장합니다. 이렇게 하면 코드가 더 직관적이고 명확하기 때문에, 개발자가 실수를 일으킬 여지가 줄어듭니다.

# 위 코드는 이렇게 세 줄로 풀어서 사용할 수 있습니다.
people = ["최재우", "신민철"]
people_isin = data["담당"].isin(people)

data[people_isin]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
4 강성희 2019-01-04 NaN kg 최재우 여의도 75.8 76.0 76.2
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8

이번에는 색인에서 응용할 수 있는 새로운 기능을 하나 더 배워보겠습니다. 이번에 배울 기능은 str.contains입니다. 이 기능은 보통 문자열 컬럼에서 자주 사용하는데, 문자열 컬럼에 특정 문자열이 포함(contains) 되어있을 경우에는 True, 아닌 경우에는 False를 반환합니다.

# 담당이 김씨이면 True, 아니면 False로 간주합니다.
kim_contains = data["담당"].str.contains("김")

# 판다스의 색인 기능을 실행하면 담당자가 김씨인 기록만 가져올 수 있습니다.
data[kim_contains]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
8 강성희 2019-01-08 NaN kg 김민철 여의도 75.8 75.8 75.0
9 강성희 2019-01-09 75.0 kg 김민철 여의도 75.0 75.8 NaN

이번에는 컬럼 안의 내용이 비어있는지 여부를 확인하겠습니다.

판다스는 비어있는 데이터를 NaN이라고 표기합니다. NaN은 Not a Number의 약자라고 보시면 됩니다. 또는 비어있는 데이터를 null이라고도 표현합니다.

다음은 몸무게가 비어있는 기록만을 반환하는 코드입니다.

# 몸무게가 비어있으면(NaN) True, 비어있지 않으면 False를 반환합니다.
null_weight = data["몸무게"].isnull()

# 판다스의 색인 기능을 실행하면 몸무게가 비어있는(NaN) 데이터만을 반환합니다.
data[null_weight]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
4 강성희 2019-01-04 NaN kg 최재우 여의도 75.8 76.0 76.2
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8
8 강성희 2019-01-08 NaN kg 김민철 여의도 75.8 75.8 75.0

정 반대의 기능은 notnull 입니다. 이 기능은 몸무게가 비어있지 않으면 True, 비어있으면 False를 반환합니다.

# 몸무게가가 비어있지 않으면 True, 비어있으면 False를 반환합니다.
weight_exists = data["몸무게"].notnull()

data[weight_exists]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8
9 강성희 2019-01-09 75.0 kg 김민철 여의도 75.0 75.8 NaN

다른 방식으로도 not_null을 표현할 수 있습니다. 바로 물결 표시(~)입니다. 물결 표시(~)는 판다스를 색인할 때 True를 False로, False를 True로 뒤집어줍니다. 이렇게 하면 not_null을 사용해도 물결 표시(~)를 통해 null과 동일한 효과를 낼 수 있습니다.

# not_null을 사용하였지만 물결 표시(~)로 결과를 뒤집어줬습니다.
# 그런고로 isnull과 동일한 결과가 나옵니다.
null_weight = ~data["몸무게"].notnull()

# 색인을 실행하면 몸무게가 비어있는(NaN) 기록만 가져옵니다.
data[null_weight]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
4 강성희 2019-01-04 NaN kg 최재우 여의도 75.8 76.0 76.2
7 강성희 2019-01-07 NaN kg 최재우 여의도 75.8 75.7 75.8
8 강성희 2019-01-08 NaN kg 김민철 여의도 75.8 75.8 75.0

색인을 할 때는 여러 조건을 동시에 넣을 수 있습니다. 여러 조건을 넣을 때는 &(and, shift + 7)와 |(or, shift + \ (대괄호 닫는 버튼 오른쪽에 있는 버튼))를 사용합니다.

한 가지 주의해야 할 점은, 조건을 여러개 넣을 땐 서로간의 충돌이 생길 수 있기 때문에 언제나 조건마다 괄호를 넣어줘야 합니다. 다만 조건을 여러 개의 변수로 쪼개서 사용하면 괄호를 사용하지 않아도 무방합니다.

# 많은 분들이 실수하시는 판다스의 색인을 잘못 사용한 사례
# and는 파이썬의 문법이기 때문에 판다스에서는 동작하지 않습니다. 판다스에서는 and가 아닌 & 표시를 사용합니다.
data[data["몸무게"] > 75.5 and data["담당"] == "김지수"]
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-255-ef035e1e50c3> in <module>
      1 # 많은 분들이 실수하시는 판다스의 색인을 잘못 사용한 사례
      2 # and는 파이썬의 문법이기 때문에 판다스에서는 동작하지 않습니다. 판다스에서는 and가 아닌 & 표시를 사용합니다.
----> 3 data[data["몸무게"] > 75.5 and data["담당"] == "김지수"]


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/generic.py in __nonzero__(self)
   1574         raise ValueError("The truth value of a {0} is ambiguous. "
   1575                          "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
-> 1576                          .format(self.__class__.__name__))
   1577 
   1578     __bool__ = __nonzero__


ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
# 위 코드에서 and는 &로 바꿔줘야 합니다.
# 하지만 판다스에서는 문법상의 이유로 인해 두 개의 조건을 &로 묶으면 잘 동작하지 않는 경우가 있습니다.
data[data["몸무게"] > 75.5 & data["담당"] == "김지수"]
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in na_op(x, y)
   1303         try:
-> 1304             result = op(x, y)
   1305         except TypeError:


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in rand_(left, right)
    148 def rand_(left, right):
--> 149     return operator.and_(right, left)
    150 


TypeError: unsupported operand type(s) for &: 'float' and 'str'


During handling of the above exception, another exception occurred:


TypeError                                 Traceback (most recent call last)

~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in na_op(x, y)
   1320                 try:
-> 1321                     result = libops.scalar_binop(x, y, op)
   1322                 except:


pandas/_libs/ops.pyx in pandas._libs.ops.scalar_binop()


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in rand_(left, right)
    148 def rand_(left, right):
--> 149     return operator.and_(right, left)
    150 


TypeError: unsupported operand type(s) for &: 'bool' and 'str'


During handling of the above exception, another exception occurred:


TypeError                                 Traceback (most recent call last)

<ipython-input-256-929009af1e72> in <module>
      1 # 위 코드에서 and는 &로 바꿔줘야 합니다.
      2 # 하지만 판다스에서는 문법상의 이유로 인해 두 개의 조건을 &로 묶으면 잘 동작하지 않는 경우가 있습니다.
----> 3 data[data["몸무게"] > 75.5 & data["담당"] == "김지수"]


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in wrapper(self, other)
   1358                       is_integer_dtype(np.asarray(other)) else fill_bool)
   1359 
-> 1360             res_values = na_op(self.values, other)
   1361             unfilled = self._constructor(res_values, index=self.index)
   1362             return filler(unfilled).__finalize__(self)


~/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/pandas/core/ops.py in na_op(x, y)
   1324                                     "with a scalar of type [{typ}]"
   1325                                     .format(dtype=x.dtype,
-> 1326                                             typ=type(y).__name__))
   1327 
   1328         return result


TypeError: cannot compare a dtyped [object] array with a scalar of type [bool]
# 그래서 언제나 &(and)나 |(or)를 사용할때는 모든 조건을 괄호로 묶어줘야 잘 동작합니다.
# 아래 코드는 담당이 김지수님이고, 몸무게가 75.5 kg 이상인 모든 기록을 가져옵니다.
data[(data["몸무게"] > 75.5) & (data["담당"] == "김지수")]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
# 위 코드를 풀어서 여러 줄의 코드로 나눠서 사용했습니다.
# 이 방식이 앞선 방식보다 훨씬 직관적이며 간결합니다. 그러므로 아래와 같은 방식으로 코드를 작성하는걸 추천드립니다.
high_weight = data["몸무게"] > 75.5
person_kim = data["담당"] == "김지수"

data[high_weight & person_kim]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
# &(and)와 동일한 방식으로 or(|)를 사용할 수 있습니다.
# 아래 코드는 담당이 김지수님이거나, 그렇지 않다면 몸무게가 75.5 kg 이상인 모든 기록을 가져옵니다.
data[high_weight | person_kim]
이름 측정일 몸무게 단위 담당 지점 몸무게(fill) 몸무게(fill)(prev) 몸무게(fill)(next)
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 76.4 NaN 75.7
2 강성희 2019-01-02 75.7 kg 김지수 강남 75.7 76.4 76.0
3 강성희 2019-01-03 76.0 kg 김지수 강남 76.0 75.7 75.8
5 강성희 2019-01-05 76.2 kg 김지수 강남 76.2 75.8 75.7
6 강성희 2019-01-06 75.7 kg 최재우 여의도 75.7 76.2 75.8

판다스의 색인과 행렬검색의 loc를 응용하면, 특정 조건으로 컬럼을 검색하는 것도 가능합니다.

# 담당이 김지수인 모든 기록의 몸무게를 가져옵니다.
# loc를 사용하면 이렇게 조건으로 색인을 한 뒤 컬럼을 검색할 수도 있습니다.
data.loc[data["담당"] == "김지수", "몸무게"]
회차
1    76.4
2    75.7
3    76.0
5    76.2
Name: 몸무게, dtype: float64
# 비슷한 방식으로 이번에는 여러 개의 컬럼('이름', '측정일', '몸무게')를 검색합니다.
data.loc[data["담당"] == "김지수", ["이름", "측정일", "몸무게"]]
이름 측정일 몸무게
회차
1 강성희 2019-01-01 76.4
2 강성희 2019-01-02 75.7
3 강성희 2019-01-03 76.0
5 강성희 2019-01-05 76.2

pivot_table

이번에 다룰 통계분석 기능은 pivot_table입니다. pivot_table은 우리가 엑셀(Excel)에서 자주 다루던 그 pivot_table과 거의 동일한 기능입니다.

# pivot_table을 실행합니다. index에는 담당 컬럼을, values에는 몸무게 컬럼을 집어넣습니다.
# 이렇게 하면 각 담당자마다의 평균 몸무게가 나옵니다.
pd.pivot_table(data, index="담당", values="몸무게")
몸무게
담당
김민철 75.000
김지수 76.075
최재우 75.700

pivot_table에서 index, values 모두 여러 개의 컬럼을 추가할 수 있습니다. 이 경우에는 리스트([])를 사용합니다.

# pivot_table을 실행합니다. 이번에는 index에 지점과 담당 컬럼을 동시에 집어넣습니다.
# 이렇게 하면 각 지점과 담당자마다의 평균 몸무게가 나옵니다.
pd.pivot_table(data, index=["지점", "담당"], values="몸무게")
몸무게
지점 담당
강남 김지수 76.075
여의도 김민철 75.000
최재우 75.700

만일 평균(mean)이 아닌 다른 통계 연산을 하고 싶다면, aggfunc 옵션을 사용하면 됩니다.

# pandas에 이은, numpy 라는 새로운 패키지를 읽어옵니다.
# numpy는 공식적으로는 선형대수(linear algebra) 패키지이지만, 지금은 편하게 수학 연산을 쉽게 해주는 패키지라고 이해하셔도 무방합니다.
import numpy as np

# pivot_table을 실행합니다. 이번에는 aggfunc에 np.sum을 집어넣습니다.
# 이렇게 하면 각 지점과 담당자마다의 몸무게의 누적치(sum)가 나옵니다.
pd.pivot_table(data, index=["지점", "담당"], values="몸무게", aggfunc=np.sum)
몸무게
지점 담당
강남 김지수 304.3
여의도 김민철 75.0
최재우 75.7

컬럼 추가 & 수정

판다스에서 컬럼을 추가하는 기능과 수정하는 기능은 간단합니다. 재미있는 건 1) 컬럼을 추가하거나 수정하는데 배워야 하는 기능은 거의 없으며, 2) 컬럼을 추가하는 코드와 수정하는 코드는 사실상 동일합니다.

아래의 코드에서는 컬럼을 추가하는 기능만 다루도록 하겠습니다. (하지만 동일한 코드를 컬럼을 수정할 때 사용할 수 있습니다)

새로운 컬럼을 추가하고 수정할 수 있습니다.

# 컬럼 안에 들어가는 값이 모두 동일하다면, 파이썬에서 변수에 값을 할당하는 것과 동일한 문법으로 컬럼을 추가할 수 있습니다.
# 아래 코드는 '지역'이라는 이름의 새로운 컬럼을 추가하고, 해당 컬럼에 "서울"이라는 값을 대입합니다.
data["지역"] = "서울"

# 데이터의 지역 컬럼만 출력합니다.
# 컬럼 안에 있는 모든 값에 '서울' 이라고 들어가 있는 것을 확인할 수 있습니다.
data[["지역"]]
지역
회차
1 서울
2 서울
3 서울
4 서울
5 서울
6 서울
7 서울
8 서울
9 서울

판다스에서 컬럼을 추가하는 가장 기본적인 룰은 갯수만 맞으면 무조건 들어간다 입니다. 가령 현재 데이터의 개수는 9개인데, 어떤 데이터 형식이건 상관 없이 9개로 된 데이터를 넣어주면 새로운 컬럼을 추가할 수 있습니다.

# 아이디라는 새로운 컬럼을 추가하고, 여기에 파이썬의 리스트를를 사용하여 1부터 9까지의 숫자를 대입합니다.
data["아이디"] = [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 데이터의 아이디 컬럼만 출력합니다.
# 1부터 시작해서 값이 하나씩 늘어나며 추가되는 것을 확인할 수 있습니다.
data[["아이디"]]
아이디
회차
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9

이제부터 위에서 배운 내용을 응용하여 컬럼을 추가하는 다양한 방법에 대해 살펴보겠습니다.

먼저 kg을 gram으로 바꿔주는 방식입니다. 판다스의 데이터는 기본적인 연산은 대부분 지원되므로, 편하게 1,000을 곱해준 뒤 새로운 컬럼에 추가해주면 됩니다.

# '몸무게' 컬럼에 100을 곱해줍니다. 이를 '몸무게(gram)'라는 이름의 새로운 컬럼에 추가해줍니다.
data["몸무게(gram)"] = data["몸무게"] * 100

# 몸무게(gram) 컬럼이 새롭게 추가된 것을 알 수 있습니다.
data[["몸무게", "몸무게(gram)"]]
몸무게 몸무게(gram)
회차
1 76.4 7640.0
2 75.7 7570.0
3 76.0 7600.0
4 NaN NaN
5 76.2 7620.0
6 75.7 7570.0
7 NaN NaN
8 NaN NaN
9 75.0 7500.0

하지만 현실적으로, 판다스에서 컬럼을 추가할 때 가장 많이 하는 것은 조건마다 컬럼을 추가하는 것입니다. 가령 “A라는 조건에는 1이라는 값을 넣어주고, B라는 조건에는 2라는 값을 넣어준다.”라는 방식을 사용하는 경우가 많습니다.

이렇게 조건별로 컬럼을 추가해주는 방식은 크게 두 가지로 나뉠 수 있는데, 첫 번째는 각 조건마다 컬럼을 하나씩 추가하는 방법이며, 두 번째는 컬럼은 하나인데 그 안에 조건마다 값이 다르게 들어가는 방법입니다. 먼저 첫 번째 방식으로 컬럼을 추가해보겠습니다.

# 몸무게가 75.6 kg보다 크면 True이고, 그렇지 않으면 False인 데이터를 만듭니다.
# 이 결과를 '비만'이라는 이름의 새로운 컬럼에 추가합니다.
data["비만"] = data["몸무게"] > 75.6

# '몸무게'와 '비만' 컬럼을 동시에 출력합니다.
data[["몸무게", "비만"]]
몸무게 비만
회차
1 76.4 True
2 75.7 True
3 76.0 True
4 NaN False
5 76.2 True
6 75.7 True
7 NaN False
8 NaN False
9 75.0 False

이번에는 2번 방식으로 컬럼을 추가해보겠습니다. 1번 방식과 비슷하지만, 이번에는 ‘상태’라는 컬럼을 추가한 뒤, 75.6 kg 보다 크면 ‘비만’, 그렇지 않으면 ‘정상’이라는 값이 들어가는 컬럼을 추가해보겠습니다.

data.loc[data["몸무게"] > 75.6, "상태"] = "비만"
data.loc[data["몸무게"] <= 75.6, "상태"] = "정상"

data[["몸무게", "비만", "상태"]]
몸무게 비만 상태
회차
1 76.4 True 비만
2 75.7 True 비만
3 76.0 True 비만
4 NaN False NaN
5 76.2 True 비만
6 75.7 True 비만
7 NaN False NaN
8 NaN False NaN
9 75.0 False 정상

이번에는 위의 방식을 응용하여 구역을 세부적으로 나눠보겠습니다. ‘몸무게’ 컬럼을 활용하여 새로운 컬럼을 만들고 싶습니다.

  • 몸무게가 75.5 kg 미만이면 목표 초과 달성
  • 몸무게가 75.5 kg 이상이되, 76.0 kg 이하면 목표 달성
  • 몸무게가 76.0 kg 초과이면 목표 달성 실패

이 방식이 방식도 1번 방식과 2번 방식 모두 가능합니다. 먼저 1번 방식으로 해보겠습니다.

# 몸무게가 75.5 kg 미만이면 True, 아니면 False인 리스트를 만듭니다.
# 이 결과를 목표초과라는 이름의 새로운 컬럼에 할당합니다.
data["목표초과"] = data["몸무게"] < 75.5

# 비슷한 방식으로, 몸무게가 75.5 kg 이상이고 76.0 kg 미만이면 True, 아니면 False인 리스트를 만듭니다.
# 마찬가지로 목표달성라는 이름의 새로운 컬럼에 할당합니다.
data["목표달성"] = (data["몸무게"] >= 75.5) & (data["몸무게"] <= 76.0)

# 마찬가지로, 몸무게가 76.0 kg 초과면 True, 아니면 False인 리스트를 만듭니다.
# 이를 목표실패는 컬럼에 할당합니다.
data["목표실패"] = data["몸무게"] > 76.0

# 몸무게와 그 관련 컬럼(목표초과, 목표달성, 목표실패)을 출력합니다.
data[["몸무게", "목표초과", "목표달성", "목표실패"]]
몸무게 목표초과 목표달성 목표실패
회차
1 76.4 False False True
2 75.7 False True False
3 76.0 False True False
4 NaN False False False
5 76.2 False False True
6 75.7 False True False
7 NaN False False False
8 NaN False False False
9 75.0 True False False

이번에는 2번 방식으로 해보겠습니다.

# 1) data["몸무게"] < 75.5  먼저 판다스의 색인 기능을 활용해서, 몸무게가 75.5 kg 미만이면 True, 그렇지 않으면 False인 리스트를 만듭니다.
# 2) data[data["몸무게"] < 75.5]  이 리스트를 통해 몸무게가 75.5kg 미만인 데이터를 색인하고, 
# 3) data.loc[data["몸무게"] < 75.5, "목표"] 이 후 판다스 행렬 검색에서 사용한 loc를 활용하여, '목표'라는 이름의 컬럼에 접근합니다.
# 4) data.loc[data["몸무게"] < 75.5, "목표"] = "초과"  비록 '목표'라는 이름의 컬럼은 없지만, 이 컬럼에 "초과"라는 데이터를 넣음으로서 새로운 컬럼을 생성합니다.
data.loc[data["몸무게"] < 75.5, "목표"] = "초과"

# 유사한 방식으로 몸무게 75.5 kg 이상, 76.0 kg 이하일 경우 '목표' 컬럼에 달성이라는 값을 넣습니다.
data.loc[(data["몸무게"] >= 75.5) & (data["몸무게"] <= 76.0), "목표"] = "달성"

# 비슷하게 운임요금이 76.0 kg 이상인 경우 '목표' 컬럼에 달성이라는 값을 넣습니다.
data.loc[data["몸무게"] > 76.0, "목표"] = "미만"

# '몸무게'와 '목표' 컬럼을 출력합니다.
data[["몸무게", "목표"]]
몸무게 목표
회차
1 76.4 미만
2 75.7 달성
3 76.0 달성
4 NaN NaN
5 76.2 미만
6 75.7 달성
7 NaN NaN
8 NaN NaN
9 75.0 초과

지금까지 배운 내용을 응용한다면, 다음과 같은 방식으로 빈 값(NaN)에 원하는 값을 채워넣을 수 있습니다.

# '몸무게' 컬럼이 비어있는(NaN, null) 데이터의 '비만' 컬럼에 빈 값(NaN, np.nan)을 채워넣습니다.
data.loc[data["몸무게"].isnull(), "비만"] = np.nan

# '몸무게' 컬럼과 '비만' 컬럼을 함께 보여줍니다.
data[["몸무게", "비만"]]
몸무게 비만
회차
1 76.4 1.0
2 75.7 1.0
3 76.0 1.0
4 NaN NaN
5 76.2 1.0
6 75.7 1.0
7 NaN NaN
8 NaN NaN
9 75.0 0.0

그리고 조건별로 컬럼을 추가할때도, 등호(=)의 우측에 하나의 값이 아닌 여러 개의 값을 넣어주는 것도 가능합니다.

# 담당자명이 김지수인 데이터만 가져와서, 1회차부터 순서대로 값을 넣어줍니다. 값을 넣어줄 때는 파이썬의 리스트를 활용합니다.
data.loc[data["담당"] == "김지수", "담당회차"] = [1, 2, 3, 4]

# 담당자명이 최재우인 데이터만 가져와서, 1회차부터 순서대로 값을 넣어줍니다. 값을 넣어줄 때는 파이썬의 리스트를 활용합니다.
data.loc[data["담당"] == "최재우", "담당회차"] = [1, 2, 3]

# 담당자명이 김민철인 데이터만 가져와서, 1회차부터 순서대로 값을 넣어줍니다. 값을 넣어줄 때는 파이썬의 리스트를 활용합니다.
data.loc[data["담당"] == "김민철", "담당회차"] = [1, 2]

# '담당' 컬럼과 '담당회차' 컬럼을 출력합니다.
data[["담당", "담당회차"]]
담당 담당회차
회차
1 김지수 1.0
2 김지수 2.0
3 김지수 3.0
4 최재우 1.0
5 김지수 4.0
6 최재우 2.0
7 최재우 3.0
8 김민철 1.0
9 김민철 2.0

컬럼 삭제하기

컬럼을 삭제하는 노하우는 간단합니다. 크게 두 가지 방법이 있는데, 첫 번째 방법은 우리가 이전에 배운 행렬 검색을 조금만 응용하면 되고, 두 번째 방법은 판다스의 drop이라는 기능을 사용하면 됩니다.

가령 위에서 새롭게 추가한 몸무게 관련 컬럼들(‘몸무게(fill)’, ‘몸무게(fill)(prev)’, ‘몸무게(fill)(next)’, ‘몸무게(gram)’)과 비만 관련 컬럼들(‘비만’, ‘상태’, ‘목표초과’, ‘목표달성’, ‘목표실패’, ‘목표’)을 제거해보겠습니다.

# 새롭게 가져올 컬럼명이 들어있는 리스트를 생성합니다.
# 이 리스트에는 다음의 컬럼이 제거되어 있습니다.
# 몸무게 관련 컬럼들('몸무게(fill)', '몸무게(fill)(prev)', '몸무게(fill)(next)', '몸무게(gram)')
# 비만 관련 컬럼들('비만', '상태', '목표초과', '목표달성', '목표실패', '목표')
columns = ['이름', '측정일', '몸무게', '단위', '담당', '지점', '지역', '아이디']

# 앞서 생성한 columns 리스트를 활용해 필요한 컬럼만 검색합니다.
# 이후 그 결과를 자기 자신에 대입합니다.
data = data[columns]

# 이제 국적 관련 컬럼과 운임요금 관련 컬럼이 삭제되어 있는 것을 확인할 수 있습니다.
data
이름 측정일 몸무게 단위 담당 지점 지역 아이디
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 서울 1
2 강성희 2019-01-02 75.7 kg 김지수 강남 서울 2
3 강성희 2019-01-03 76.0 kg 김지수 강남 서울 3
4 강성희 2019-01-04 NaN kg 최재우 여의도 서울 4
5 강성희 2019-01-05 76.2 kg 김지수 강남 서울 5
6 강성희 2019-01-06 75.7 kg 최재우 여의도 서울 6
7 강성희 2019-01-07 NaN kg 최재우 여의도 서울 7
8 강성희 2019-01-08 NaN kg 김민철 여의도 서울 8
9 강성희 2019-01-09 75.0 kg 김민철 여의도 서울 9

두 번째 방법은 판다스의 drop이라는 기능을 활용하는 방법입니다. 아래의 코드는 data 데이터프레임에서 ‘지역’이라는 이름의 컬럼을 제거합니다.

# data 데이터프레임에서 '지역'이라는 이름의 컬럼을 제거합니다.
# drop으로 컬럼을 제거할때는 언제나 axis=1이라는 옵션을 넣어줘야 합니다.
# (반대로 axis=0은 컬럼이 아닌 로우(행)을 제거하는 역할을 합니다)
data.drop("지역", axis=1)
이름 측정일 몸무게 단위 담당 지점 아이디
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남 1
2 강성희 2019-01-02 75.7 kg 김지수 강남 2
3 강성희 2019-01-03 76.0 kg 김지수 강남 3
4 강성희 2019-01-04 NaN kg 최재우 여의도 4
5 강성희 2019-01-05 76.2 kg 김지수 강남 5
6 강성희 2019-01-06 75.7 kg 최재우 여의도 6
7 강성희 2019-01-07 NaN kg 최재우 여의도 7
8 강성희 2019-01-08 NaN kg 김민철 여의도 8
9 강성희 2019-01-09 75.0 kg 김민철 여의도 9

판다스의 drop을 사용하면 여러 개의 컬럼을 동시에 지울 수도 있습니다.

# data 데이터프레임에서 '지역'과 '아이디'라는 이름의 컬럼을 제거합니다.
# 여러 컬럼을 동시에 지울때는 컬럼명을 리스트로 넣어주면 됩니다.
data.drop(["지역", "아이디"], axis=1)
이름 측정일 몸무게 단위 담당 지점
회차
1 강성희 2019-01-01 76.4 kg 김지수 강남
2 강성희 2019-01-02 75.7 kg 김지수 강남
3 강성희 2019-01-03 76.0 kg 김지수 강남
4 강성희 2019-01-04 NaN kg 최재우 여의도
5 강성희 2019-01-05 76.2 kg 김지수 강남
6 강성희 2019-01-06 75.7 kg 최재우 여의도
7 강성희 2019-01-07 NaN kg 최재우 여의도
8 강성희 2019-01-08 NaN kg 김민철 여의도
9 강성희 2019-01-09 75.0 kg 김민철 여의도

Apply

판다스에는 apply라고 하는 기능이 있습니다. 쉽게 설명하자면 판다스 버전의 반복문(for)이라고 볼 수 있는데, 판다스의 문법만으로 해결하기 어려운 일이 있다면 판다스의 apply를 시도해볼 수 있습니다.

# band_weight라는 이름의 파이썬 함수를 만듭니다.
# 이 함수에서는 weight라는 값을 인자로 받습니다.
def band_weight(weight):
    # weight 변수에 NaN이 들어왔으면 별도의 처리를 하지 않고 NaN으로 반환합니다.
    if pd.isnull(weight):
        return np.nan

    # weight 변수에 있는 값이 75.5 kg 미만이면 '초과'라는 값을 반환합니다.
    if weight < 75.5:
        return '초과'
    # weight 변수에 있는 값이 75.5 kg 이상이고 76.0 kg 이하면 '달성'이라는 값을 반환합니다.
    elif weight >= 75.5 and weight < 76.0:
        return '달성'
    # 모든 조건해 해당하지 않으면 '미달'이라는 값을 반환합니다.
    else:
        return '미달'

# Name 컬럼에 apply를 실행합니다. 인자로는 band_weight라는 함수를 집어넣습니다.
# 이렇게 하면 전체 목표 데이터 값에 대해 band_weight를 실행한 뒤 그 결과를 반환합니다.
# 반환한 결과를 목표라는 이름의 새로운 컬럼에 집어넣습니다.
data["목표"] = data["몸무게"].apply(band_weight)

# 이름(Name)과 호칭(Title) 컬럼을 출력하여 두 가지를 비교합니다.
data[["몸무게", "목표"]]
/Users/shayne.kang/.pyenv/versions/3.6.7/envs/dsschool/lib/python3.6/site-packages/ipykernel_launcher.py:21: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
몸무게 목표
회차
1 76.4 미달
2 75.7 달성
3 76.0 미달
4 NaN NaN
5 76.2 미달
6 75.7 달성
7 NaN NaN
8 NaN NaN
9 75.0 초과

파일 저장하기

마지막으로 배울 것은 파일 저장하기입니다. 파일을 저장할 때는 to_csv라는 함수를 활용해 저장할 수 있습니다. 다음은 판다스에서 데이터를 저장하는 방식입니다.

# data 변수에 있는 데이터프레임을 data.csv라는 이름으로 저장합니다.
data.to_csv("C:/Users/User/Desktop/python/기초.csv")

이후 data.csv를 보면 수정한 데이터가 성공적으로 저장되어 있는 것을 확인할 수 있습니다.

Data Frame 만들기

d = {'col1': [1, 2], 'col2': [3, 4]}
df = pd.DataFrame(data=d)
df
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-1-b9917b5b527d> in <module>
      1 d = {'col1': [1, 2], 'col2': [3, 4]}
----> 2 df = pd.DataFrame(data=d)
      3 df


NameError: name 'pd' is not defined
df1 = pd.DataFrame(columns =['col1','col2'])

df1['col1'] = [1,2]
df1['col2'] = [3,4]

Categories:

Updated:

Comments