pandas 기초
학습 내용
- 표 데이터를 읽고 구조를 확인하는 방법 익히기
- 필요한 행과 열을 고르고 정렬하고 요약하는 방법 익히기
- 결측치, 중복, 문자열, 날짜처럼 자주 만나는 데이터 정리 방법 익히기
- 표를 합치고 모양을 바꾸고 저장하는 기본 흐름 익히기
pandas는 표 형태 데이터를 읽고 정리하고 요약하는 라이브러리입니다. 엑셀 표를 코드로 다룬다고 생각하면 이해하기 쉽습니다.
이 페이지에서는 초보자가 가장 자주 쓰는 데이터 확인, 선택, 정제, 요약, 변형 흐름을 순서대로 익힙니다.
이 블록의 역할은 pandas가 다루는 가장 기본 구조인 DataFrame과 Series를 이해하는 것입니다. 보통 외부 파일 없이 작은 예시 데이터를 직접 만들거나, 메소드가 어떤 모양의 데이터를 받는지 먼저 확인할 때 사용합니다.
pandas는 외부 파일을 읽기 전에, 코드 안에서 직접 표를 만드는 것부터 시작할 수도 있습니다. 가장 기본이 되는 객체는 두 가지입니다.
DataFrame: 여러 열을 가진 표Series: 한 줄로 된 열 데이터
pd.DataFrame()은 보통 딕셔너리나 리스트를 넣어서 만듭니다. 딕셔너리의 키는 열 이름이 되고, 각 리스트의 값들이 아래로 한 줄씩 들어갑니다.
import pandas as pd data = { "name": ["민지", "준호", "서연"], "score": [90, 80, 95], } df = pd.DataFrame(data) print(df)
실행 결과:
name score 0 민지 90 1 준호 80 2 서연 95
위 코드에서 name과 score는 열 이름입니다. 각 리스트 안의 값은 같은 위치끼리 한 행을 만듭니다. 첫 번째 행은 민지, 90, 두 번째 행은 준호, 80입니다.
리스트 안에 딕셔너리를 여러 개 넣어서 표를 만들 수도 있습니다.
rows = [ {"city": "서울", "temp": 18}, {"city": "부산", "temp": 20}, ] df = pd.DataFrame(rows) print(df)
pd.Series()는 한 열짜리 데이터라고 생각하면 됩니다.
s = pd.Series([10, 20, 30], name="score") print(s)
Series는 값 하나하나가 순서대로 들어 있는 구조이고, DataFrame은 여러 Series가 모여 표가 된 구조라고 이해하면 됩니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 외부 파일을 DataFrame으로 가져오는 시작점을 익히는 것입니다. 보통 분석을 처음 시작할 때, 큰 파일에서 필요한 열만 일부 읽고 싶을 때, 구분자나 인코딩이 다른 파일을 열 때 사용합니다.
실제 문제에서는 이미 만들어진 CSV 파일을 읽는 경우가 더 많습니다. 이때 가장 자주 쓰는 메소드가 pd.read_csv()입니다.
import pandas as pd data_path = "/data/worldcup.csv" df = pd.read_csv(data_path) print(df.head(2))
여기서 가장 중요한 부분은 df = pd.read_csv(data_path)입니다. 읽어 온 표 전체를 df 변수에 저장해 두고, 그 다음부터 df.head(), df.columns, df["Player"]처럼 계속 df를 기준으로 작업합니다. 파일을 읽기만 하고 변수에 저장하지 않으면 뒤에서 표를 다루기 어렵습니다.
pd.read_csv()에서 자주 쓰는 파라미터는 아래와 같습니다.
filepath_or_buffer: 읽을 파일 경로나 문자열 버퍼sep: 값과 값을 구분하는 문자usecols: 필요한 열만 골라서 읽기nrows: 앞에서 몇 줄만 읽기index_col: 특정 열을 인덱스로 사용하기encoding: 파일 문자 인코딩 지정하기
예를 들어 sep=","는 쉼표로 구분된 일반적인 CSV를 읽을 때 사용합니다. 탭으로 구분된 파일은 sep="\t"처럼 씁니다. usecols=["Player", "Goals"]를 넣으면 그 두 열만 읽습니다. nrows=3을 넣으면 앞의 세 줄만 읽습니다. index_col="Player"를 넣으면 Player 열이 왼쪽 인덱스 자리에 들어갑니다. 한글 CSV를 읽을 때 글자가 깨지면 encoding="utf-8"이나 encoding="cp949"를 지정하는 경우가 많습니다.
import pandas as pd data_path = "/data/worldcup.csv" df = pd.read_csv(data_path, usecols=["Player", "Goals"], nrows=3) print(df)
실행 결과:
Player Goals 0 Miroslav Klose 16 1 Ronaldo 15 2 Gerd Muller 14
위 코드는 Player, Goals 두 열만 읽고, 앞의 세 줄만 가져온 결과입니다. 이런 식으로 read_csv() 단계에서 미리 필요한 범위만 읽어 오면 이후 작업이 훨씬 단순해집니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 읽어 온 표가 어떤 구조인지 빠르게 점검하는 것입니다. 보통 read_csv() 직후에 데이터가 제대로 읽혔는지 확인할 때, 전처리 전후로 행 개수나 열 이름이 바뀌었는지 비교할 때 사용합니다.
파일을 읽은 다음에는 표의 구조를 먼저 확인해야 합니다. 이때 가장 자주 보는 속성과 메소드는 아래와 같습니다.
df.head(n): 앞에서n개 행 보기df.tail(n): 뒤에서n개 행 보기df.shape:(행 개수, 열 개수)확인df.columns: 열 이름 확인df.index: 행 번호 확인
열 하나를 고를 때는 df["열이름"]처럼 씁니다.
import pandas as pd data_path = "/data/worldcup.csv" df = pd.read_csv(data_path) print(df["Player"].head(3).tolist()) print(df.shape) print(list(df.columns)) print(list(df.index[:3]))
실행 결과:
['Miroslav Klose', 'Ronaldo', 'Gerd Muller'] (1295, 4) ['Player', 'Goals', 'Years', 'Country'] [0, 1, 2]
첫 줄은 Player 열의 앞 3개 값입니다. 둘째 줄은 이 표가 1295행 4열이라는 뜻입니다. 셋째 줄은 열 이름 목록이고, 넷째 줄은 현재 인덱스가 0, 1, 2, ...처럼 붙어 있다는 뜻입니다.
열을 여러 개 고를 때는 대괄호를 한 번 더 써서 df[["Player", "Goals"]]처럼 적습니다. 이 형태는 값 하나가 아니라 “열 두 개짜리 표”를 남겨 둡니다.
print(df[["Player", "Goals"]].shape)
실행 결과:
(1295, 2)
위 결과는 Player, Goals 두 열만 남은 표라서 열 개수가 2라는 뜻입니다. 열 하나를 고를지, 여러 열을 표 형태로 남길지에 따라 다음 단계에서 쓸 수 있는 코드가 달라집니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 숫자 열과 문자 열의 전체 분위기를 짧게 요약하는 것입니다. 보통 평균이나 최댓값을 빠르게 확인할 때, 이상치가 있는지 감을 잡을 때, 보고서 전에 데이터 특징을 먼저 살펴볼 때 사용합니다.
숫자 열 하나를 고른 뒤에는 바로 요약 메소드를 많이 사용합니다.
sum(): 합계mean(): 평균max(): 최댓값min(): 최솟값
import pandas as pd data_path = "/data/body.csv" df = pd.read_csv(data_path) print(round(df["체중 : kg"].mean(), 2)) print(df["측정나이"].min())
실행 결과:
67.43 21.0
첫 줄은 전체 사람들의 평균 체중이 67.43kg이라는 뜻이고, 둘째 줄은 이 데이터에서 가장 어린 측정 나이가 21세라는 뜻입니다. 이렇게 숫자 열 하나를 골라 요약 메소드를 쓰면, 표 전체를 직접 보지 않아도 중요한 특징을 빠르게 파악할 수 있습니다.
더 많은 요약값을 한 번에 보고 싶을 때는 describe()를 사용합니다.
print(df.describe()) print(df.describe(include="all"))
기본 describe()를 실행하면 숫자형 열에 대해 count, mean, std, min, 25%, 50%, 75%, max 같은 요약표가 나옵니다. describe(include="all")를 실행하면 여기에 문자형 열도 함께 포함되어, 예를 들어 성별 열에서는 unique, top, freq 같은 값이 추가로 보입니다.
기본 describe()는 숫자형 열만 요약합니다. 반면 include="all"을 넣으면 문자형 열도 함께 보게 됩니다. 이때는 mean, max 대신 unique, top, freq 같은 정보도 함께 나타납니다.
include="all": 숫자형과 문자형을 함께 요약- 문자형 열에서
unique: 서로 다른 값 개수 - 문자형 열에서
top: 가장 자주 나온 값
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 표에서 원하는 행과 열을 정확한 기준으로 집어내는 법을 익히는 것입니다. 보통 특정 행 하나를 확인할 때, 범위를 잘라 볼 때, 나중에 특정 위치의 값을 수정할 때 사용합니다.
행과 열의 위치를 더 구체적으로 고를 때는 loc[]와 iloc[]를 씁니다.
loc[]: 라벨 기준iloc[]: 위치 번호 기준
지금처럼 기본 인덱스가 0, 1, 2, ...인 경우에는 둘이 비슷해 보일 수 있습니다. 하지만 슬라이싱 규칙이 다릅니다.
import pandas as pd data_path = "/data/worldcup.csv" df = pd.read_csv(data_path) print(list(df.loc[0:2, "Player"])) print(list(df.iloc[0:2, 0]))
실행 결과:
['Miroslav Klose', 'Ronaldo', 'Gerd Muller'] ['Miroslav Klose', 'Ronaldo']
첫 줄은 loc[0:2]가 0, 1, 2행을 모두 포함해서 선수 3명이 나온 결과입니다. 둘째 줄은 iloc[0:2]가 끝 위치 2를 포함하지 않아서 0, 1행만 나온 결과입니다. 두 메소드가 비슷해 보여도 슬라이싱 규칙이 다르다는 점을 출력 결과가 바로 보여 줍니다.
loc[0:2]는 끝 번호 2를 포함하지만, iloc[0:2]는 끝 번호 2를 포함하지 않습니다. 이 차이는 아주 자주 헷갈리는 부분이라 꼭 익혀 두는 편이 좋습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 조건에 맞는 행만 남겨서 분석 대상을 좁히는 것입니다. 보통 특정 집단만 따로 보고 싶을 때, 두세 가지 조건을 동시에 만족하는 데이터만 뽑을 때, 이상한 값을 가진 행을 찾아낼 때 사용합니다.
조건으로 행을 고를 때는 보통 아래 형태를 씁니다.
df[df["열이름"] == 값]
예를 들어 Legendary가 True인 포켓몬만 보고 싶다면 이렇게 씁니다.
import pandas as pd data_path = "/data/pokemon.csv" df = pd.read_csv(data_path) result = df[df["Legendary"] == True] print(result.shape[0])
이 코드를 실행하면 Legendary가 True인 행만 남고, 마지막 shape[0]가 그 행 개수를 숫자로 보여 줍니다. 즉 “조건으로 먼저 줄인 뒤, 남은 행 수를 센다”는 흐름입니다.
조건이 두 개 이상이면 &와 |를 사용합니다.
&: 그리고|: 또는
이때는 조건마다 괄호를 꼭 붙이는 습관을 들이면 좋습니다.
df[(df["Generation"] == 1) & (df["Legendary"] == True)]
이 코드는 Generation이 1이면서 동시에 Legendary도 True인 행만 남깁니다. &는 두 조건을 모두 만족해야 한다는 뜻이고, 각 조건을 괄호로 묶어 두어야 pandas가 조건식을 올바르게 해석합니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 원하는 기준대로 데이터를 순서대로 다시 배치하는 것입니다. 보통 상위값과 하위값을 찾을 때, 정렬된 결과를 표처럼 읽기 쉽게 다시 번호 매길 때, 동점 처리 기준까지 분명하게 만들 때 사용합니다.
정렬할 때는 sort_values()를 사용합니다.
df.sort_values("Goals") df.sort_values("Goals", ascending=False)
첫 번째 코드는 Goals를 작은 값부터 정렬하고, 두 번째 코드는 큰 값부터 정렬합니다. 즉 ascending 값 하나만 바꿔도 오름차순과 내림차순을 바꿀 수 있습니다.
여기서 ascending 파라미터는 정렬 방향을 뜻합니다.
ascending=True: 오름차순ascending=False: 내림차순
정렬한 뒤에는 원래 인덱스 번호가 그대로 남는 경우가 많습니다. 이때 인덱스를 다시 0, 1, 2, ...로 정리하고 싶으면 reset_index(drop=True)를 사용합니다.
sorted_df = df.sort_values("점수", ascending=False).reset_index(drop=True) print(sorted_df.loc[0, "나라명"])
이 코드를 실행하면 점수가 가장 높은 나라가 맨 위 0번 행으로 올라오고, 그 나라명이 출력됩니다. reset_index(drop=True)를 붙이지 않으면 원래 인덱스 번호가 남아서, 정렬 결과를 읽을 때 초보자가 헷갈리기 쉽습니다.
drop=True를 붙이면 예전 인덱스를 새 열로 남기지 않고 버립니다.
동점이 있을 수 있는 데이터는 정렬 기준을 두 개 이상 주는 편이 안전합니다.
df.sort_values(["Total", "Name"], ascending=[False, True])
이 코드는 먼저 Total이 큰 순서로 정렬하고, Total이 같은 경우에는 Name을 가나다순처럼 오름차순으로 다시 정렬합니다. 정렬 기준을 여러 개 주면 동점 처리 방식까지 분명하게 만들 수 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 범주형 열 안에 어떤 값들이 들어 있고 얼마나 자주 나오는지 파악하는 것입니다. 보통 코드값 종류를 확인할 때, 예상치 못한 값이 섞여 있는지 볼 때, 분포가 한쪽으로 치우쳤는지 확인할 때 사용합니다.
범주형 열을 볼 때 자주 쓰는 메소드가 세 가지 있습니다.
unique(): 실제로 어떤 값들이 있는지 보기nunique(): 서로 다른 값이 몇 개인지 세기value_counts(): 각 값이 몇 번씩 나왔는지 세기
import pandas as pd data_path = "/data/pokemon.csv" df = pd.read_csv(data_path) print(df["Type 1"].nunique()) print(df["Type 1"].value_counts().head())
첫 줄은 Type 1 종류가 몇 개인지 숫자 하나로 보여 줍니다. 둘째 줄은 각 타입이 몇 번 나왔는지 많은 순서대로 일부를 보여 주는 표입니다. 즉 nunique()는 “종류 수”, value_counts()는 “값별 빈도표”를 만든다고 보면 됩니다.
unique()는 배열처럼 보이고, nunique()는 숫자를 줍니다. value_counts()는 “값별 개수표”를 만들어 줍니다. 범주형 데이터를 빠르게 훑어볼 때 아주 자주 씁니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 비어 있는 값을 찾아내고, 분석이 가능하도록 적절한 값으로 메우는 것입니다. 보통 모델링 전에 누락값을 정리할 때, 문자열 열에는 안내용 값을 넣고 숫자 열에는 평균 같은 대표값을 넣을 때 사용합니다.
실제 데이터에는 비어 있는 값이 자주 있습니다. pandas에서는 이를 결측치라고 보고 다룹니다.
isna(): 값이 비어 있는지 확인fillna(): 비어 있는 값을 다른 값으로 채우기
import pandas as pd data_path = "/data/pokemon.csv" df = pd.read_csv(data_path) print(df["Type 2"].isna().sum()) print(df["Type 2"].fillna("없음").head())
첫 줄은 Type 2 열에 비어 있는 값이 몇 개인지 세어 준 결과입니다. 둘째 줄은 결측치를 "없음"으로 바꾼 뒤 앞부분만 보여 주는 결과입니다. 즉 isna().sum()은 결측치 개수를 확인하는 용도이고, fillna()는 결측치를 다른 값으로 바꿔서 다루기 쉽게 만드는 용도입니다.
숫자형 결측치는 평균으로 채우는 경우가 많고, 문자형 결측치는 "없음", "미확인" 같은 문자열로 채우는 경우가 많습니다.
df["부패에 대한인식"].fillna(df["부패에 대한인식"].mean())
이 코드를 실행하면 비어 있던 칸들이 이 열의 평균값으로 채워진 새로운 Series가 만들어집니다. 원본 df가 자동으로 바뀌는 것은 아니므로, 실제로 사용하려면 변수에 다시 담거나 원래 열에 대입해야 합니다.
위 코드는 비어 있는 값을 해당 열의 평균으로 채웁니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 같은 범주끼리 데이터를 묶어서 그룹별 요약값을 만드는 것입니다. 보통 성별별 평균, 연도별 합계, 지역별 개수처럼 “무엇별로” 결과를 보고 싶을 때 사용합니다.
groupby()는 같은 값을 가진 행끼리 묶어서 요약할 때 사용합니다.
import pandas as pd data_path = "/data/body.csv" df = pd.read_csv(data_path) result = df.groupby("측정회원성별")["체중 : kg"].mean() print(result)
실행 결과는 보통 성별이 인덱스로 붙고, 각 성별에 대응하는 평균 체중 값이 옆에 나오는 짧은 요약표 형태입니다. 즉 원래 긴 표를 그대로 보는 것이 아니라, 같은 성별끼리 묶은 뒤 평균만 남긴 결과라고 이해하면 됩니다.
이 코드는 성별별 평균 체중을 계산합니다. groupby("측정회원성별")로 먼저 그룹을 만들고, 그다음 ["체중 : kg"] 열만 골라 mean()을 계산한 것입니다.
groupby() 뒤에는 아래 메소드들을 자주 이어 붙입니다.
mean()sum()count()max()min()
처음에는 groupby("기준열")["계산할열"].메소드() 형태를 통째로 익히는 것이 가장 쉽습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 개별 메소드를 따로 아는 수준에서 끝나지 않고, 실제 전처리 흐름처럼 이어서 읽는 습관을 만드는 것입니다. 보통 집계한 뒤 정렬하고, 정렬한 뒤 인덱스를 정리하고, 마지막 값을 꺼내는 식의 실제 작업에서 사용합니다.
실제 데이터 분석에서는 메소드 하나만 단독으로 쓰는 경우보다, 여러 개를 이어서 쓰는 경우가 더 많습니다.
예를 들어 아래처럼 쓸 수 있습니다.
import pandas as pd data_path = "/data/happy.csv" df = pd.read_csv(data_path) result = df.groupby("년도")["점수"].mean() result = result.sort_values(ascending=False).reset_index() print(result.loc[0, "년도"])
이 코드를 실행하면 먼저 년도별 평균 점수가 계산되고, 그 평균 점수가 큰 순서대로 정렬된 뒤, 맨 위 행의 년도가 출력됩니다. 긴 메소드 체인이 나와도 “요약 -> 정렬 -> 인덱스 정리 -> 값 꺼내기” 순서로 끊어서 읽으면 이해하기 쉽습니다.
이 코드는 아래 순서로 읽으면 됩니다.
년도별 평균 점수를 구한다.- 평균 점수를 큰 값부터 정렬한다.
- 인덱스를 다시 정리한다.
- 첫 번째 행의
년도를 꺼낸다.
처음에는 메소드가 길게 이어지면 어렵게 보일 수 있습니다. 그럴 때는 한 줄씩 끊어서 읽는 습관을 들이면 훨씬 이해하기 쉽습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 날짜처럼 보이는 문자열을 진짜 시간형 데이터로 바꾸는 것입니다. 보통 월별 집계, 요일 분석, 특정 기간 필터링, 시간대별 평균처럼 날짜 계산이 필요한 순간에 사용합니다.
날짜와 시간처럼 보이는 문자열도, 처음에는 그냥 문자열일 뿐입니다. 이런 값을 진짜 날짜/시간으로 바꾸려면 pd.to_datetime()을 사용합니다.
import pandas as pd data_path = "/data/weather.csv" df = pd.read_csv(data_path) df["time_dt"] = pd.to_datetime(df["time"]) print(df["time_dt"].dtype) print(df["time_dt"].dt.year.iloc[0]) print(df["time_dt"].dt.month.iloc[0]) print(df["time_dt"].dt.day.iloc[0]) print(df["time_dt"].dt.hour.iloc[0]) print(df["time_dt"].dt.date.iloc[0]) print(df["time_dt"].dt.day_name().iloc[0])
실행 결과:
datetime64[ns] 2020 1 1 0 2020-01-01 Wednesday
첫 줄은 time_dt 열이 이제 단순 문자열이 아니라 날짜/시간 타입으로 바뀌었다는 뜻입니다. 그 아래 줄들은 같은 첫 번째 시각 2020-01-01 00:00:00에서 연도, 월, 일, 시, 날짜, 요일 이름을 하나씩 꺼낸 결과입니다.
즉 첫 번째 행 기준으로 보면 아래처럼 읽을 수 있습니다.
.dt.year->2020.dt.month->1.dt.day->1.dt.hour->0.dt.date->2020-01-01.dt.day_name()->Wednesday
.dt는 한 행만 볼 때도 쓸 수 있지만, 여러 행에 한꺼번에 적용할 때 더 자주 사용합니다. 예를 들어 앞의 3개 행에 대해 월, 시각, 요일을 뽑아 보면 아래처럼 됩니다.
print(list(df["time_dt"].dt.month.head(3))) print(list(df["time_dt"].dt.hour.head(3))) print(list(df["time_dt"].dt.day_name().head(3)))
실행 결과:
[1, 1, 1] [0, 1, 2] ['Wednesday', 'Wednesday', 'Wednesday']
첫 줄은 앞의 3개 데이터가 모두 1월이라는 뜻입니다. 둘째 줄은 시각이 0시, 1시, 2시로 한 시간씩 증가하는 모습을 보여 줍니다. 셋째 줄은 같은 날짜 안에 있는 데이터들이라서 요일 이름이 모두 Wednesday로 나온 것입니다.
pd.to_datetime()로 바꾸고 나면 아래처럼 .dt를 붙여서 자주 사용하는 값을 쉽게 뽑을 수 있습니다.
.dt.year: 연도.dt.month: 월.dt.day: 일.dt.hour: 시.dt.date: 날짜 부분만 보기.dt.day_name(): 요일 이름
자주 같이 쓰는 파라미터도 알아 두면 좋습니다.
format="%Y-%m-%d %H:%M:%S": 문자열 형식을 알고 있을 때 그 형식대로 읽기errors="coerce": 잘못된 날짜를 에러 대신NaT로 바꾸기
예를 들어 형식이 일정한 문자열이라면 아래처럼 쓸 수 있습니다.
pd.to_datetime(df["time"], format="%Y-%m-%d %H:%M:%S", errors="coerce")
이 코드는 형식에 맞는 값은 정상적으로 날짜로 바꾸고, 형식이 맞지 않는 값은 NaT로 처리합니다. 초보자 단계에서는 우선 to_datetime()으로 바꾼 뒤 .dt로 연, 월, 일, 시를 꺼내는 흐름을 익히는 것이 중요합니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 집계 결과를 행과 열이 있는 요약표 형태로 다시 배치하는 것입니다. 보통 보고서용 표를 만들 때, 두 기준이 만나는 평균이나 개수를 한눈에 보고 싶을 때, groupby() 결과를 더 읽기 좋게 펴서 보고 싶을 때 사용합니다.
pivot_table()은 표를 다시 배치해서 요약표를 만들 때 사용합니다. groupby()가 묶어서 계산하는 느낌이라면, pivot_table()은 계산 결과를 행과 열로 보기 좋게 펼쳐 놓는 느낌에 가깝습니다.
import pandas as pd happy_path = "/data/happy.csv" happy = pd.read_csv(happy_path) rank_table = pd.pivot_table(happy, index="년도", values="행복랭킹", aggfunc="mean") print(rank_table) pokemon_path = "/data/pokemon.csv" pokemon = pd.read_csv(pokemon_path) total_table = pd.pivot_table( pokemon, index="Generation", columns="Legendary", values="Total", aggfunc="mean", fill_value=0, ) print(round(total_table.loc[1, True], 2))
실행 결과:
행복랭킹 년도 2018 78.5 2019 78.5 663.33
첫 번째 출력은 년도를 행으로 두고 행복랭킹 평균을 계산한 요약표입니다. 두 번째 출력은 Generation과 Legendary를 기준으로 Total 평균을 계산한 뒤, 그중 1세대이면서 Legendary=True인 값을 꺼낸 결과입니다.
pivot_table()에서 자주 보는 파라미터는 아래와 같습니다.
index: 행 방향 기준columns: 열 방향 기준values: 계산할 값이 들어 있는 열aggfunc:mean,sum,count처럼 어떤 계산을 할지fill_value: 비어 있는 칸을 어떤 값으로 채울지
처음에는 pd.pivot_table(df, index=..., values=..., aggfunc=...) 형태로 시작하고, 익숙해지면 columns=와 fill_value=를 추가하는 순서로 익히는 것이 좋습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 서로 다른 두 표를 공통 키를 기준으로 하나로 합치는 것입니다. 보통 회원 정보와 거래 정보처럼 서로 다른 테이블을 연결할 때, 한 표에 없는 열을 다른 표에서 가져와 붙일 때 사용합니다.
merge()는 공통된 기준 열을 이용해 두 표를 옆으로 합칠 때 사용합니다. 데이터베이스의 조인과 같은 개념이라고 생각하면 됩니다.
import pandas as pd left = pd.DataFrame( { "id": [1, 2, 3], "name": ["민지", "준호", "서연"], } ) right = pd.DataFrame( { "id": [1, 3, 4], "score": [90, 85, 70], } ) print(pd.merge(left, right, on="id").to_string(index=False)) print() print(pd.merge(left, right, on="id", how="left").to_string(index=False))
실행 결과:
id name score 1 민지 90 3 서연 85 id name score 1 민지 90.0 2 준호 NaN 3 서연 85.0
첫 번째 출력은 기본값인 inner merge라서 두 표에 모두 있는 id만 남은 결과입니다. 둘째 출력은 how="left"라서 왼쪽 표의 행을 모두 유지하고, 오른쪽에 없는 값은 NaN으로 남긴 결과입니다.
merge()에서 자주 보는 옵션은 아래와 같습니다.
on="id": 두 표에서 같은 이름의 기준 열을 사용할 때how="inner": 양쪽에 모두 있는 키만 남기기how="left": 왼쪽 표 기준으로 모두 남기기how="right": 오른쪽 표 기준으로 모두 남기기how="outer": 양쪽 키를 모두 남기기left_on=...,right_on=...: 기준 열 이름이 서로 다를 때
처음에는 pd.merge(left, right, on="기준열")부터 익히고, 그다음 how=와 left_on/right_on을 추가하는 방식이 가장 이해하기 쉽습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 표를 위아래나 좌우로 이어 붙이는 것입니다. 보통 월별 파일을 한 표로 모을 때, 따로 계산한 열을 기존 표 옆에 붙일 때, 구조가 비슷한 결과를 한 번에 합칠 때 사용합니다.
concat()은 표를 위아래로 붙이거나, 옆으로 나란히 붙일 때 사용합니다. 기준 열을 맞춰 조인하는 merge()와 달리, concat()은 순서대로 이어 붙이는 느낌에 가깝습니다.
import pandas as pd first = pd.DataFrame( { "name": ["민지", "준호"], "score": [80, 90], } ) second = pd.DataFrame( { "name": ["서연"], "score": [95], } ) city = pd.DataFrame( { "city": ["서울", "부산"], } ) print(list(pd.concat([first, second]).index)) print(list(pd.concat([first, city], axis=1).columns))
실행 결과:
[0, 1, 0] ['name', 'score', 'city']
첫 줄은 위아래로 붙이는 기본 concat이 원래 인덱스를 그대로 가져와서 마지막 행 인덱스가 다시 0이 된다는 뜻입니다. 둘째 줄은 axis=1을 주면 표가 옆으로 붙으면서 열이 name, score, city로 늘어난다는 뜻입니다.
concat()에서 자주 보는 옵션은 아래와 같습니다.
- 기본값
axis=0: 행 방향으로 위아래 붙이기 axis=1: 열 방향으로 옆으로 붙이기ignore_index=True: 새로0, 1, 2, ...인덱스 만들기
열 구조가 서로 다르면 없는 칸은 NaN으로 채워집니다. 그래서 concat()을 쓴 뒤에는 shape, columns, isna().sum()으로 결과를 한 번 확인하는 습관이 중요합니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 분석에 필요 없는 행이나 열을 깔끔하게 제거하는 것입니다. 보통 설명용 열을 버릴 때, 잘못 들어간 특정 행을 지울 때, 전처리 후 최소한의 열만 남기고 싶을 때 사용합니다.
drop()은 필요 없는 행이나 열을 제거할 때 사용합니다. 초보자 단계에서는 columns=로 열을 지우고, index=로 행을 지우는 두 가지를 먼저 익히면 충분합니다.
import pandas as pd data_path = "/data/worldcup.csv" df = pd.read_csv(data_path) print(list(df.drop(columns=["Country"]).columns)) print(df.drop(index=0).iloc[0]["Player"])
실행 결과:
['Player', 'Goals', 'Years'] Ronaldo
첫 줄은 Country 열을 제거한 뒤 남은 열 이름입니다. 둘째 줄은 0번 행을 제거했기 때문에, 그다음 첫 번째 행의 선수가 Ronaldo가 된 결과입니다.
drop()에서 초보자가 가장 자주 쓰는 형태는 아래와 같습니다.
df.drop(columns=["열이름"]): 열 제거df.drop(index=번호): 행 제거
행을 지운 뒤에는 인덱스가 자동으로 다시 매겨지지 않는다는 점도 같이 기억해 두면 좋습니다. 필요하면 뒤에서 배우는 reset_index()를 이어서 사용합니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 같은 값이 반복된 행을 정리해서 유일한 데이터만 남기는 것입니다. 보통 회원 번호나 나라명처럼 한 번만 남겨야 하는 기준 열이 있을 때, 중복 제거 규칙을 명확히 두고 정리할 때 사용합니다.
drop_duplicates()는 중복된 행을 제거할 때 사용합니다. 어떤 열을 기준으로 중복을 볼지 subset=으로 정하고, 어느 행을 남길지 keep=으로 조절합니다.
import pandas as pd data_path = "/data/happy.csv" df = pd.read_csv(data_path) print(df.drop_duplicates(subset="나라명").shape[0]) print(df.drop_duplicates(subset="나라명", keep=False).shape[0])
실행 결과:
160 8
첫 줄은 나라명 기준으로 중복을 제거하고 첫 번째 값만 남긴 뒤의 행 개수입니다. 둘째 줄은 keep=False를 사용해서 중복된 값은 모두 버리고, 한 번만 등장한 나라만 남긴 결과입니다.
자주 쓰는 옵션은 아래와 같습니다.
subset="열이름": 어떤 열을 기준으로 중복을 볼지 정하기keep="first": 처음 나온 값 남기기keep="last": 마지막 값 남기기keep=False: 중복된 값은 전부 제거하기
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 열의 자료형을 계산에 맞는 형태로 명확히 바꾸는 것입니다. 보통 문자열 숫자를 실제 숫자로 바꿔 계산해야 할 때, True/False를 0과 1로 바꿔 합계를 내고 싶을 때 사용합니다.
astype()은 열의 자료형을 바꿀 때 사용합니다. 문자열을 숫자로 바꾸거나, 불리언을 0과 1로 바꾸는 식으로 자주 사용합니다.
import pandas as pd world_path = "/data/worldcup.csv" world = pd.read_csv(world_path) pokemon_path = "/data/pokemon.csv" pokemon = pd.read_csv(pokemon_path) print(world["Goals"].astype(str).iloc[0]) print(pokemon["Legendary"].astype(int).sum())
실행 결과:
16 65
첫 줄은 숫자였던 Goals 값을 문자열로 바꾼 뒤 꺼낸 결과입니다. 둘째 줄은 True/False였던 Legendary를 1/0으로 바꿨기 때문에, 합계를 구하면 전설 포켓몬 수가 됩니다.
초보자 단계에서는 아래 변환을 가장 자주 만나게 됩니다.
astype(str): 문자열로 바꾸기astype(int): 정수로 바꾸기astype(float): 실수로 바꾸기
자료형을 바꾼 뒤에는 계산 결과가 달라질 수 있으므로, head()나 dtype을 함께 확인하는 습관이 좋습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 값 하나를 다른 값 하나로 간단하게 치환하는 것입니다. 보통 코드값을 한글 라벨로 바꿀 때, M/F, True/False처럼 읽기 어려운 값을 보기 쉬운 값으로 바꿀 때 사용합니다.
map()은 열의 값을 다른 값으로 바꿀 때 사용합니다. 코드값을 한글 이름으로 바꾸거나, True/False를 읽기 쉬운 문자열로 바꿀 때 아주 편리합니다.
import pandas as pd data_path = "/data/body.csv" df = pd.read_csv(data_path) mapped = df["측정회원성별"].map({"M": "남성", "F": "여성"}) print(mapped.iloc[0]) print(list(mapped.drop_duplicates()))
실행 결과:
남성 ['남성', '여성']
첫 줄은 M이 남성으로 바뀐 결과입니다. 둘째 줄은 이 매핑을 전체 열에 적용했을 때 어떤 값들로 바뀌는지를 보여 줍니다.
map()은 보통 사전과 함께 씁니다.
df["측정회원성별"].map({"M": "남성", "F": "여성"})
사전에 없는 값은 NaN이 될 수 있으므로, 실제로는 바뀐 값이 제대로 들어갔는지 value_counts()나 head()로 함께 확인하는 편이 좋습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 문자열 안의 일부 문자나 단어를 다른 형태로 바꾸는 것입니다. 보통 날짜 구분자를 통일할 때, 불필요한 기호를 제거할 때, 파일명이나 라벨에 맞는 형식으로 텍스트를 고칠 때 사용합니다.
문자열 열에서 특정 글자나 단어를 바꾸고 싶을 때는 str.replace()를 사용합니다. 날짜 구분자를 바꾸거나, 공백을 밑줄로 바꾸는 식으로 자주 활용합니다.
import pandas as pd weather_path = "/data/weather.csv" weather = pd.read_csv(weather_path) happy_path = "/data/happy.csv" happy = pd.read_csv(happy_path) print(weather["time"].str.replace("-", "/", regex=False).iloc[0]) print( happy[happy["나라명"] == "United Arab Emirates"]["나라명"] .str.replace(" ", "_", regex=False) .iloc[0] )
실행 결과:
2020/01/01 00:00:00 United_Arab_Emirates
첫 줄은 날짜 문자열의 -를 /로 바꾼 결과입니다. 둘째 줄은 나라명 안의 공백을 _로 바꾼 결과입니다.
처음에는 문자열을 그대로 바꾸는 경우가 많으므로 regex=False를 같이 적어 두면 더 명확합니다. 그러면 정규식이 아니라 “보이는 문자 그대로” 바꾼다는 뜻이 됩니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 텍스트 안에 특정 단어가 포함되어 있는지 검사하는 조건을 만드는 것입니다. 보통 상품명, 나라명, 설명 문장처럼 긴 문자열 열에서 원하는 패턴이 들어간 행만 찾을 때 사용합니다.
문자열 안에 특정 단어나 글자가 들어 있는지 확인할 때는 str.contains()를 사용합니다. 조건 필터링과 같이 쓰면 텍스트 검색 문제를 쉽게 풀 수 있습니다.
import pandas as pd pokemon_path = "/data/pokemon.csv" pokemon = pd.read_csv(pokemon_path) world_path = "/data/worldcup.csv" world = pd.read_csv(world_path) print(pokemon["Name"].str.contains("Mega").sum()) print(world["Country"].str.contains("land").sum())
실행 결과:
49 169
첫 줄은 이름에 Mega가 들어 있는 포켓몬 수입니다. 둘째 줄은 나라명에 land가 들어 있는 행 개수입니다. 즉 str.contains()는 조건식을 만들 때 많이 쓰이는 문자열 검색 메소드입니다.
결측치가 섞여 있을 수 있는 열에서는 na=False를 함께 넣는 경우도 많습니다. 그러면 비어 있는 값은 False로 처리됩니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 복잡해진 인덱스를 다시 평평한 일반 표 형태로 돌리는 것입니다. 보통 필터링이나 정렬 뒤 번호가 어수선해졌을 때, groupby() 결과를 다시 일반 열 구조로 바꾸고 싶을 때 사용합니다.
reset_index()는 행 번호를 다시 0, 1, 2, ...로 정리하거나, 인덱스로 올라간 값을 다시 일반 열로 내릴 때 사용합니다.
import pandas as pd world_path = "/data/worldcup.csv" world = pd.read_csv(world_path) happy_path = "/data/happy.csv" happy = pd.read_csv(happy_path) filtered = world[world["Country"] == "Brazil"].reset_index(drop=True) grouped = happy.groupby("년도")["점수"].mean().reset_index() print(filtered.loc[0, "Player"]) print(list(grouped.columns))
실행 결과:
Ronaldo ['년도', '점수']
첫 줄은 필터링으로 비어 버린 인덱스를 drop=True로 새로 정리한 뒤 첫 번째 선수를 꺼낸 결과입니다. 둘째 줄은 groupby() 결과의 인덱스였던 년도가 다시 일반 열로 내려온 결과입니다.
reset_index()는 아래 두 경우에 특히 많이 사용합니다.
- 필터링이나 정렬 뒤 인덱스를 다시 깔끔하게 정리할 때
groupby()결과처럼 인덱스로 올라간 값을 다시 열로 바꿀 때
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 넓은 표를 긴 표로 바꿔서 후속 분석이 쉬운 형태로 만드는 것입니다. 보통 여러 측정 열을 하나의 값 열로 모으고 싶을 때, 시각화 도구가 long format을 요구할 때 사용합니다.
melt()는 가로로 넓게 퍼진 표를 세로로 길게 바꾸는 메소드입니다. 여러 열에 흩어진 값을 “열 이름”과 “값” 형태로 다시 모아 주기 때문에, 시각화나 추가 집계를 하기 전에 자주 사용합니다.
import pandas as pd data_path = "/data/weather.csv" df = pd.read_csv(data_path) small = df[["time", "이화동기온", "수영동기온"]].head(2) melted = small.melt(id_vars="time", var_name="지역", value_name="기온") print(melted.shape) print(list(melted.columns))
실행 결과:
(4, 3) ['time', '지역', '기온']
원래는 time마다 기온 열이 두 개였지만, melt()를 한 뒤에는 지역 열과 기온 열로 길게 정리된 표가 됩니다. 즉 여러 측정 열을 하나의 값 열로 모으는 작업이라고 이해하면 쉽습니다.
자주 쓰는 파라미터는 아래와 같습니다.
id_vars: 그대로 남길 기준 열var_name: 원래 열 이름이 들어갈 새 열 이름value_name: 실제 값이 들어갈 새 열 이름
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 두 범주형 변수의 조합이 몇 번씩 나오는지 빠르게 세는 것입니다. 보통 성별과 등급, 세대와 전설 여부처럼 두 범주가 만나는 빈도를 한눈에 보고 싶을 때 사용합니다.
crosstab()은 두 범주형 변수의 교차 빈도표를 만들 때 사용합니다. pivot_table(..., aggfunc='count')와 비슷하지만, “개수표를 빠르게 만든다”는 목적이 더 분명합니다.
import pandas as pd body_path = "/data/body.csv" body = pd.read_csv(body_path) pokemon_path = "/data/pokemon.csv" pokemon = pd.read_csv(pokemon_path) print(pd.crosstab(body["측정회원성별"], body["등급"]).loc["M", "A"]) print(pd.crosstab(pokemon["Generation"], pokemon["Legendary"]).loc[1, True])
실행 결과:
1865 6
첫 줄은 남성(M)이면서 A 등급인 사람 수입니다. 둘째 줄은 1세대이면서 전설 포켓몬(True)인 개수입니다. 즉 crosstab()은 두 기준이 만나는 칸의 빈도를 빠르게 확인하는 메소드입니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 전처리하거나 집계한 결과를 다시 파일로 저장하는 것입니다. 보통 분석 결과를 다른 사람에게 전달할 때, 중간 전처리 결과를 별도 파일로 남길 때, 나중에 다시 읽기 위한 결과물을 만들 때 사용합니다.
to_csv()는 DataFrame이나 Series를 CSV 파일로 저장할 때 사용합니다. 분석 결과를 파일로 내보내거나, 전처리한 데이터를 다시 저장할 때 아주 자주 쓰게 됩니다.
가장 기본적인 저장은 아래처럼 할 수 있습니다.
import pandas as pd data_path = "/data/worldcup.csv" df = pd.read_csv(data_path) df.head(3).to_csv("worldcup_top3.csv")
이렇게 저장하면 파일 내용은 보통 아래처럼 됩니다.
,Player,Goals,Years,Country 0,Miroslav Klose,16,2002-2006-2010-2014,Germany 1,Ronaldo,15,1998-2002-2006,Brazil 2,Gerd Muller,14,1970-1974,Germany
맨 앞의 빈 열 이름과 0, 1, 2는 DataFrame의 인덱스가 함께 저장된 결과입니다. 처음에는 이 부분이 왜 생기는지 헷갈리기 쉬운데, to_csv()는 기본적으로 인덱스도 데이터의 일부처럼 같이 저장한다고 이해하면 됩니다.
실무에서 가장 자주 같이 붙는 옵션이 index=False입니다.
df.head(3).to_csv("worldcup_top3_no_index.csv", index=False)
이렇게 저장하면 파일 내용은 아래처럼 바뀝니다.
Player,Goals,Years,Country Miroslav Klose,16,2002-2006-2010-2014,Germany Ronaldo,15,1998-2002-2006,Brazil Gerd Muller,14,1970-1974,Germany
즉 index=False는 맨 앞의 인덱스 열을 저장하지 않겠다는 뜻입니다. 다른 사람에게 CSV를 전달하거나, 나중에 다시 읽을 때 불필요한 인덱스 열이 생기지 않게 하려면 이 옵션을 자주 사용합니다.
특정 열만 골라 저장할 수도 있습니다.
df.head(3).to_csv( "worldcup_player_goals.csv", columns=["Player", "Goals"], index=False, )
이 경우 저장되는 파일은 아래처럼 필요한 열만 남습니다.
Player,Goals Miroslav Klose,16 Ronaldo,15 Gerd Muller,14
여기서 columns=[...]는 저장할 열을 직접 지정하는 옵션입니다. 분석 결과 중 일부 열만 따로 저장하고 싶을 때 유용합니다.
이 블록의 역할은 실제 외부 데이터 파일을 실패 없이 읽기 위한 기본 점검 포인트를 익히는 것입니다. 보통 실무에서 받은 파일이 쉼표가 아니라 탭으로 구분되어 있거나, 한글이 깨지거나, 인덱스 구조가 예상과 다를 때 사용합니다.
실전 전처리에서는 파일 형식이 항상 똑같지 않습니다. 어떤 파일은 쉼표로 구분되고, 어떤 파일은 탭으로 구분됩니다. 한글 데이터는 인코딩을 맞춰 주어야 정상적으로 읽히는 경우도 많습니다.
탭으로 구분된 파일은 sep="\t"를 사용합니다.
import pandas as pd data_path = "/data/problems.csv" df = pd.read_csv(data_path, sep="\t") print(df.shape)
한글 파일에서 글자가 깨질 때는 encoding=을 지정합니다.
import pandas as pd data_path = "/data/problems.csv" df = pd.read_csv(data_path, encoding="euc-kr") print(df.tail(3))
파일을 읽은 뒤에는 shape, columns, index를 함께 확인하면 구조를 빠르게 파악할 수 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 전처리를 시작하기 전에 각 열의 정체를 진단하는 것입니다. 보통 어떤 열이 숫자형인지 문자형인지 구분해야 할 때, 결측치와 자료형을 한 번에 점검할 때, 분위수로 범위를 보고 싶을 때 사용합니다.
실전 전처리에서는 각 열의 자료형을 확인하는 일이 매우 중요합니다. 숫자처럼 보여도 문자열일 수 있고, 날짜처럼 보여도 아직 문자열일 수 있습니다. 이때 자주 쓰는 도구가 dtype, dtypes, info(), select_dtypes(), quantile()입니다.
df["열"].dtype: 한 열의 자료형 확인df.dtypes: 전체 열의 자료형 확인df.info(): 데이터 개수와 자료형을 한 번에 확인df.select_dtypes(...): 특정 자료형 열만 고르기quantile(): 분위수 계산
info()는 표를 반환하는 메소드가 아니라 화면에 정보를 출력하는 메소드입니다. 그래서 필요하면 io.StringIO()로 출력 결과를 받아서 일부 줄만 확인할 수도 있습니다.
import io import pandas as pd data_path = "/data/problems.csv" df = pd.read_csv(data_path, encoding="euc-kr") buffer = io.StringIO() df.info(buf=buffer) print(buffer.getvalue().splitlines()[1]) print(df["평균 속도"].quantile(0.75) - df["평균 속도"].quantile(0.25))
첫 줄은 인덱스 정보이고, 둘째 줄은 평균 속도 열의 IQR입니다. IQR은 3사분위수에서 1사분위수를 뺀 값입니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 원본 문자열 열을 계산 가능한 새 열로 바꾸는 것입니다. 보통 금액, 비율, 코드처럼 문자 모양으로 들어온 값을 숫자로 바꿔 평균과 정렬, 필터링에 쓰고 싶을 때 사용합니다.
실전 데이터에서는 숫자가 바로 숫자형으로 들어 있지 않은 경우가 많습니다. 금액 데이터가 "$2.39"처럼 문자열로 들어 있으면 계산을 하기 전에 숫자로 바꿔야 합니다.
import pandas as pd data_path = "/data/problems.csv" df = pd.read_csv(data_path) df["new_price"] = df["item_price"].str[1:].astype("float") print(df["new_price"].head())
str[1:]는 맨 앞의 $를 떼는 역할을 합니다. 그다음 astype("float")로 실수형으로 바꾸면 평균, 정렬, 조건 필터링 같은 계산을 바로 할 수 있습니다. 이렇게 만든 결과를 새 열에 저장해 두면 이후 문제가 훨씬 단순해집니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 조건에 맞는 행만 골라서 값을 직접 수정하는 것입니다. 보통 잘못된 라벨을 다른 값으로 바꿀 때, 결측치를 특정 문자열로 채울 때, 일부 행만 규칙에 따라 수정할 때 사용합니다.
loc[]는 행과 열을 고르는 데에도 쓰지만, 조건에 맞는 값만 바꾸는 데에도 매우 자주 사용합니다.
import pandas as pd data_path = "/data/problems.csv" df = pd.read_csv(data_path) df["new_price"] = df["item_price"].str[1:].astype("float") df.loc[df["item_name"] == "Izze", "item_name"] = "Fizzy Lizzy" print(df.head(3))
결측치를 세거나 바꾸는 것도 같은 흐름으로 자주 처리합니다.
print(df["choice_description"].isnull().sum()) df.loc[df["choice_description"].isnull(), "choice_description"] = "NoData"
즉 loc[조건, "열이름"] = 값 형태는 실전 전처리의 기본 문법이라고 생각하면 됩니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 그룹별 개수, 여러 통계, 넓은 요약표를 한 번에 다루는 것입니다. 보통 단순 평균을 넘어서 빈도표를 만들고 싶을 때, 여러 통계를 한 표에 넣고 싶을 때, 다중 인덱스 결과를 가로로 펼쳐 보고 싶을 때 사용합니다.
groupby() 뒤에는 mean()만 붙는 것이 아닙니다. 실전에서는 개수를 세는 size(), 여러 통계를 한 번에 보는 agg(), 계층 인덱스를 펼치는 unstack()도 자주 사용합니다.
import pandas as pd data_path = "/data/problems.csv" df = pd.read_csv(data_path) counts = df.groupby("host_name").size() summary = df.groupby("neighbourhood_group")["price"].agg(["mean", "var", "max", "min"]) wide = df.groupby(["neighbourhood", "neighbourhood_group"]).price.mean().unstack()
size()는 그룹별 행 개수를 세고, agg()는 여러 통계를 한 번에 계산합니다. unstack()은 행 인덱스 하나를 열로 펼쳐서 넓은 표로 바꿉니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
이 블록의 역할은 미리 준비된 메소드만으로 해결되지 않는 사용자 정의 규칙을 데이터에 적용하는 것입니다. 보통 조건문이 길어질 때, 여러 열을 함께 보면서 새 값을 계산해야 할 때, 단순 치환보다 복잡한 기준이 필요할 때 사용합니다.
map()은 값 하나를 다른 값 하나로 바꿀 때 편합니다. 반면 apply()는 직접 함수를 만들어 각 값이나 각 행에 적용할 때 자주 씁니다.
import pandas as pd data_path = "/data/problems.csv" df = pd.read_csv(data_path, index_col=0) def change_category(x): if x == "Unknown": return "N" elif x == "Less than $40K": return "a" elif x == "$40K - $60K": return "b" elif x == "$60K - $80K": return "c" elif x == "$80K - $120K": return "d" else: return "e" df["newIncome"] = df["Income_Category"].apply(change_category)
열 하나에 적용할 때는 Series.apply()를 쓰고, 행 전체를 보면서 계산할 때는 df.apply(..., axis=1)을 씁니다. 행 단위 apply()는 조건이 여러 열에 걸쳐 있을 때 특히 유용합니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.