오늘은 데이터분석 및 시각화 수업 1일차로서
데이터 분석을 하기 위해 알아야 할 기본 개념들을 정리하는 시간이었다.
오늘 배운 데이터 타입에 대한 간략한 정리와 Pandas 기본 문법들에 대해 정리하고
과제를 하며 깨달은 점을 남겨보려고 한다.
* 예시 코드 및 과제 내용의 출처는 오즈코딩스쿨이다.
데이터 타입의 분류를 나누자면 아래와 같이 정리할 수 있다.
[데이터 타입]
├─ [숫자형, Numerical Data] : 산술 연산(더하기, 빼기, 평균 등)이 가능한 데이터
│ │
│ ├─ [연속형, Continuous] : 소수점 값이 의미를 가짐
│ │ └─ 예: 키, 몸무게, 온도
│ │
│ └─ [이산형, Discrete] : 정수 값만 존재
│ └─ 예: 환자 수, 방문 횟수
│
│
└─ [범주형, Categorical Data] :산술 연산이 의미가 없는 데이터
│
├─ [순서형, Ordinal] : 범주 간 명확한 순서 or 등급이 있음
│ └─ 예: 등급, 만족도
│
└─ [명목형, Nominal] : 범주 간 순서 or 서열이 없음
└─ 예: 성별, 혈액형
데이터 타입을 올바르게 파악하고 분석을 해야 정확한 분석 결과를 얻을 수 있기 때문에
데이터 타입 개념을 정확히 알고 넘어가야 한다.
Pandas는 데이터 분석에 주로 사용되는 Python의 핵심 라이브러리로서
Numpy가 연산에 최적화된 라이브러리라면 Pandas는 좀 더 확장된 기능을 가진
숫자, 문자, 날짜 등 혼합된 데이터 처리도 가능한 아주 유연한 라이브러리이다.
오늘은 데이터 구조를 생성하는 방법, CSV파일 읽고/쓰는 방법, 인덱싱과 슬라이싱 등
기본 문법들을 정리하고, 과제 내용을 분석해보겠다.
Series는 1차원 배열이며, 인덱스(라벨)와 값의 쌍으로 구성된다.
엑셀로 보자면 하나의 '열'을 선택했을 때, 이를 Series라고 할 수 있다.
Series를 생성하는 기본 문법은 아래와 같다.
pd.Series(넣고자 하는 데이터 리스트, index=라벨 목록) # 이 때 Series의 첫 글자는 대문자를 지켜줘야 한다.
만약 index를 지정해주지 않는다면, 자동으로 0, 1, 2, ... 로 지정될 것이다.
DataFrame은 2차원 표 형태이며, 행(index)과 열(columns)로 구성된다.
생긴 형태가 엑셀 스프레드시트와 동일하다.
또한 한편으로는 여러 개의 Series를 모은 것이 DataFrame이라고 할 수 있다.
DataFrame을 생성하는 기본 문법은 아래와 같다.
# DataFrame 생성용 공통 변수
patient_names = ['김철수', '이영희', '박민수', '최지은']
columns = ['나이', '혈압', '혈당']
# method 1. 딕셔너리로 생성하기 - 컬럼 중심 데이터
dict = {
'나이': [45, 67, 32, 29], # 열 단위로 데이터 구성
'혈압': [120, 140, 110, 115],
'혈당': [95, 180, 88, 92]
}
df1 = pd.DataFrame(patient_dict, index=patient_names)
# method 2. 리스트 in 리스트로 생성 - 행별 리스트
patient_records = [
[45, 120, 95], # 김철수 (행)
[67, 140, 180], # 이영희
[32, 110, 88], # 박민수
[29, 115, 92] # 최지은
]
df2 = pd.DataFrame(patient_records, index=patient_names, columns=columns)
처음에 수업을 들을 땐 1번 방법이 더 편해보였는데,
이후 문법들이 쌓이면서 2번 방법으로 생성 연습을 했으면
인덱싱과 슬라이싱을 덜 헷갈렸을 수도 있겠다는 생각이 들었다.
health_df.to_csv('clinic_health_data.csv', # 저장할 파일명(하위 폴더에 저장할 땐 경로도 기입 필요)
index=False, # 행 번호(인덱스)는 저장하지 않음
encoding='utf-8-sig')
df = pd.read_csv('clinic_health_data.csv') # CSV 파일을 DataFrame으로 불러오기
df.head(3) # 처음 3행만 출력하여 데이터 구조 확인. 빈 괄호로 사용 시 기본값 5줄
len(df): 행 갯수df.head(n): 처음 n행(지정 안하면 기본 5)df.tail(n): 마지막 n행(지정 안하면 기본 5) df.info(): 전반적 정보(컬럼별 타입, 결측치 개수, 메모리 사용량 등 종합 정보)df.shape: 배열의 형태 (행, 열 개수) 튜플 형태 반환ndim: 배열의 차원 수 - 벡터(1차원)인지 행렬(2차원)인지 구분df.dtypes: 각 컬럼의 데이터 타입 확인df.size: 전체 요소 개수 - 총 데이터 포인트 수 파악df.describe(): 기본 통계량(count, mean, std, min, 25%, 50%, 75%, max 값 출력)df['컬럼명'].value_counts(): 범주형 데이터의 각 값별 빈도수 계산df['컬럼명'] → Series 반환df[['컬럼1', '컬럼2']] → DataFrame 반환| 구분 | iloc | loc |
|---|---|---|
| 기준 | 위치(숫자) | 라벨(문자열) or 인덱스 번호 |
| 인덱스 | 0, 1, 2... | 0, 1, 2... 또는 'P001', 'P002'... |
| 슬라이싱 | 끝 미포함 (0:3 = 0,1,2) | 끝 포함 ('A':'C' = A,B,C) |
| 사용법 | df.iloc[0] * 뒤에서 접근(음수)도 가능 | df.loc['환자A'] |
| 장점 | 위치가 명확 | 의미가 직관적 * bool mask 활용 가능 |
| 단점 | 순서 변경시 위험 | 라벨이 있어야 함 |
# iloc 문법
df.iloc[행위치, 열위치] -> 단일 데이터
df.iloc[행범위, 열범위] -> 다중 데이터
# loc 문법
df.loc[행라벨, 열라벨] -> 단일 데이터
df.loc[행범위, 열범위] -> 다중 데이터
df.loc[조건, 열라벨] -> 조건부 필터링
cf) 복합 조건 연산자의 경우, 아래의 기호를 사용하고 각 조건을 괄호로 묶어야 한다.
&: AND|: OR~: NOTdf['새 컬럼'] = 값: 단일 컬럼 추가df[['새 컬럼1'], ['새 컬럼2']] = [값1, 값2]: 다중 컬럼 추가df['새 컬럼'] = df['기존 컬럼1'] + df['기존 컬럼2']: 계산 결과 할당df.loc[조건, '새 컬럼'] = 값: 조건부 할당df['새 컬럼'] = 조건: 조건 결과 계산하여 bool값으로 추가df['컬럼']: 일괄 수정df.loc[조건, 컬럼]: 조건을 만족하는 행의 컬럼값을 선별적으로 수정df.loc[조건, [컬럼1, 컬럼2]] = [값1, 값2]: 조건을 기준으로 여러 컬럼의 값을 한 번에 수정df.loc[len(df)] = ['추가할 정보']: 인덱스 번호로 행 추가 df.loc['인덱스명'] = ['추가할 정보']: 인덱스명으로 행 추가결과 데이터프레임 = pd.concat([기존 df, 새 df], ignore_index=True): 여러 행 한번에 추가(* ignore_index=True를 작성하지 않으면, 기본값(False)으로 적용되어 인덱스 중복이 발생될 수 있다).loc[라벨명, 컬럼명]으로 접근해서 값 재할당 해주면 됨.drop('라벨명' or '컬럼명', axis=0(기본값. 행 삭제) or 1(열 삭제), inplace=False(기본값. 원본 유지) or True(원본 수정))[]로 묶기!).drop(columns=df.columns[컬럼번호]): 컬럼 번호 기준 삭제.drop(df.index[인덱스번호]): 인덱스 번호 기준 삭제.drop(df[조건].index): 조건에 부합하는 행의 인덱스 번호로 삭제.drop(index=df.index[인덱스번호], columns=['컬럼명']): 행과 컬럼 동시 삭제
[캡쳐 1-1. Pandas 1일차 과제 요구사항 및 출력 형식]

[캡쳐 1-2. 1, 2번 미션 코드 비교]
문법이 생각나지 않아 다시 수업 자료를 확인하였다.
정답 코드와 비교해 보니
DataFrame 생성 기본 문법에 .set_index('컬럼명')의 형태로 바로 이어서도 사용 가능하단걸 깨달았다.
.shape을 사용하고 인덱스 번호로 꺼내 쓸 필요 없이 len()을 활용하면 더욱 간단하다.
len()을 활용할 생각을 못했다는 것은 아직 구조 개념이 온전하게 정리되지 않았다는 의미일 것이다.
df['컬럼명']으로 데이터를 추출하는 것과 .loc[]을 사용하여 추출하는 것의 차이에 대한 개념이
아직 확실히 잡히지 않아 오류를 많이 겪었다.
특정 컬럼명을 사용하여 해당하는 모든 데이터를 가져올 때는
굳이 .loc[]을 사용하지 않는 것이 간편하다.

[캡쳐 1-3. 3~5번 미션 코드 비교]
문법이 아직 익숙지 않아 여러번 반복해서 작성해 봐야겠다.