[10일차] CSV (작성중)
2026. 6. 4. 09:03ㆍKDT/1. Python
1. CSV (Comma-Separated Values)
- 쉼표로 구분된 파일의 약자
- 데이터를 단순한 텍스트 형식으로 저장할 때 사용되는 파일 형식
1. CSV 파일 읽어오기
| read_csv("파일명") | csv 파일을 읽어온다 |
import pandas as pd
df = pd.read_csv("./광고모델_브랜드평판.csv")
type(df) # pandas.DataFrame -> 데이터프레임 형식임을 알 수 있다
2. 데이터의 간략 정보 확인 (확인할 것1)⭐
| info() | 데이터 프레임의 간략한 정보를 확인할 수 있다 |
df.info()
# <class 'pandas.DataFrame'> dataframe 자료형이야
# RangeIndex: 20 entries, 0 to 19 데이터가 20개 있어
# Data columns (total 7 columns): column은 7개야
# Non-Null Count : 데이터가 몇 개가 차있는지 / 타입은 str이야 / 혈액형은 2개가 빠졌네
# dtypes: str(7) str형 7개
# memory usage: 1.2 KB 데이터를 불러오는데 사용한 메모리량
3. 컬럼, 형태, 컬럼별 데이터 타입만 출력
| columns() | 컬럼들만 출력 |
| shape() | 데이터프레임 형태 (= 행열 개수) |
| dtypes() | 각 컬럼의 데이터 타입 |
- 컬럼은 컬럼명끼리 묶어 Index 객체로 이루어진 것을 알 수 있음
- 컬럼은 인덱스 객체로 저장되어있어 각각의 데이터 변경이 불가능함
- 변경하기 위해서는 새로 덮어씌워야 함
# df의 컬럼만 조회
df.columns
# Index(['이름', '소속사', '성별', '생년월일', '키', '혈액형', '브랜드평판지수'], dtype='str')
# 한글로 된 컬럼명을 영어로 변경
new_columns = ['name', 'company', 'gender', 'birthday', 'height', 'blood', 'brand']
df.columns = new_columns
4. 통계 정보 반환 (확인할 것2⭐)
| describe() | 통계 정보 반환. 기본값은 숫자형 |
- describe(include = str) 실행 시 출력되는 4가지 목록
| count | 데이터 개수 | ❓ 데이터가 몇 개 있는지 (include=❓) |
| unique | 고유값 개수 | 중복을 제외한 서로 다른 값 개수 |
| top | 최빈값(가장 많이 나온 값) | 최빈값이 1개면 맨 위의 데이터 출력(의미 없는 데이터가 됨) |
| freq | top 등장 횟수 | 최빈값이 몇 번 등장했는지 |
# 문자열에 대한 통계 정보를 보여줘
df.describe(include = str)
5. 원하는 개수의 데이터 조회
| head() | - 상위 5개의 열 출력 (기본값이 5) - 괄호 안의 숫자를 바꿔 원하는 만큼 조회가능 |
| tail() | - 하위 5개의 열 출력 (기본값이 5) - 괄호 안의 숫자를 바꿔 원하는 만큼 조회가능 |
df.head()
df.tail()
6. 정렬하기
| sort_index() | - index로 정렬 - 오름차순 정렬 : 기본값 - 내림차순 정렬 : 괄호 안에 ascending = False |
| sort_value(by = "컬럼명") | - 지정한 컬럼명을 기준으로 정렬 가능 - 오름차순, 내림차순 정렬 가능 - 컬럼명을 리스트로 여러 값을 넣어 정렬 가능 - na_position = 'first' : NaN 행들을 맨 위로 (기본값은 NaN을 맨 아래로) |
df.sort_index()
df.sort_index(ascending = False)
df.sort_values(by = 'height') # '키'로 오름차순 정렬
df.sort_values(by = 'height', ascending = False) # '키'로 내림차순 정렬
# height는 현재 str형임 -> 182cm와 182.2cm를 비교하게 되면 c와 .을 비교하게 됨
# -> 아스키코드 값이 c가 더 크기 때문에 182cm > 182.2cm 처럼 이상한 결과가 나올 수 있음
# str 비교는 사전(Dictionary) 순서처럼 비교
# 위 코드의 문제를 해결하기 위해 아래의 과정 실행
# 1. 컬럼명이 height인 데이터를 str형 변환 후
# 2. cm를 ''로 바꾼 후 (cm 제거)
# 3. float형으로 변환
# 4. df.info() 실행하면 height의 dtype이 float64로 바뀐 것 확인 가능
df['height'] = df['height'].str.replace("cm", "").astype(float)
df.info()
# brand 컬럼도 현재 str형이므로 int형변환
# df.info() 실행하면 brand의 dtype이 int64로 바뀐 것 확인 가능
df['brand'] = df['brand'].str.replace(",", "").astype(int)
df.info()
# 처음과 다르게 숫자형인 컬럼만 통계정보 출력
df.describe()
- describe() 실행 시 출력되는 목록
| count | 데이터 개수 | 값이 몇 개 있는지 |
| mean | 평균 | 전체 값을 더해서 나눈 값 |
| std | 표준편차 | 데이터가 얼마나 퍼져있는지 |
| min | 최소값 | 가장 작은 값 |
| 25% | 1사분위수(Q1) | 하위 25% 위치 값 |
| 50% | 중앙값(Median) | 가운데 값 |
| 75% | 3사분위수(Q3) | 상위 25 시작 값 |
| max | 최대값 | 가장 큰 값 |
# 1차 정렬 : 키(내림차순), 2차 정렬 : 브랜드(내림차순)
# NaN 값을 맨 위로
df.sort_values(by = ['height', 'brand'], ascending = [False, False], na_position = "first")
7. 인덱싱과 슬라이싱
# 컬럼명이 blood인 열 조회 (시리즈인 것을 확인 가능)
df['blood']
# 또는 df.blood
# 둘다 결과가 같지만 전자 사용 권장
# -> df['blool'] : 컬럼명이 어떤 형태든 비교적 안전하게 사용 가능하지만
# -> df.blood : 컬럼명에 한글, 공백, 특수문자가 들어있으면 사용 불가능
# blood의 타입 확인
type(df.['blood']) # pandas.Series
# 위에서 3개 데이터 출력
# 방법1
df.head(3)
# 방법2
df[:3] # 슬라이싱
# loc 인덱싱⭐
- loc : DataFrame이나 Series에서 인덱스 이름(Lable)과 컬럼명을 기준으로 데이터를 선택하는 인덱서
- 인덱스는 숫자일 수도 있고 문자일 수 도 있음
(숫자형일 필요는 없지만 unique한 값이어야 함) - loc는 사람이 읽기 쉽고 데이터 의미를 기준으로 접근하기 때문에 데이터 분석과 전처리 작업에서 가장 많이 사용
| df.loc[행에 대한 조건, 열에 대한 조건] | 각 조건은 아래와 같이 사용 가능 |
# 컬럼명이 name인 열 조회 (loc 인덱싱 사용)
df.loc[:, 'name'] # df['name']와 같은 결과
# 2부터 5까지 행 그리고 name 열들을 가져와
# 5번 포함 (2와 5는 인덱스의 숫자를 그대로 쓰는 것)
df.loc[2:5, 'name']
# 2부터 5까지 행 그리고 name, gender, height 열들을 가져와
df.loc[2:5, ['name', 'gender', 'height']]
# 2와 5행 그리고 name, gender, height 열들을 가져와
df.loc[[2, 5], ['name', 'gender', 'height']]
# 2부터 5까지 행 그리고 'name'부터 'gender'까지 열들을 가져와
# 행과 마찬가지로 'name' 컬럼부터 'gender'컬럼까지 사이의 모든 열을 조회 (gender 포함)
df.loc[2:5, 'name':'gender']
# iloc 인덱싱⭐
- DataFrame이나 Series에서 숫자 위치(Index Position) 기준으로 데이터를 선택하는 인덱서
(= 데이터의 위치를 숫자값으로 이용) - 즉, 실제 인덱스 이름이 아니라 행과 열의 순서 번호를 사용하여 데이터 가져옴
- 실제 이름과 상관 없이 순서 기준으로 접근하기 때문에 반복처리나 특정 위치 데이터 다룰 때 많이 사용
| df.loc[행에 대한 조건, 열에 대한 조건] | 각 조건은 아래와 같이 사용 가능 |
# 모든 행 그리고 0번째 열(='name') 가져와
df.iloc[:, 0] # df.loc[:, 'name'] df['name'] 두 가지와 같은 결과
# 모든 행 그리고 0부터 2까지 열을 가져와
df.iloc[:, 0:3] # 3을 포함하지 않음
# 모든 행 그리고 0과 3열을 가져와
df.iloc[:, [0, 3]]
# 1부터 4행 그리고 0부터 1열을 가져와
df.iloc[1:5, 0:2]
# height 값들이 180보다 크거나 가타은지 알려줘 (True or False로 출력)
df['height'] >= 180
# height가 180 이상인 사람만 가져와 (위 코드를 사용하여)
df[df['height'] >= 180]
# 180 이상이 사람의 모든 컬럼들 조회
# height가 180 이상인 사람 중에서 이름만 출력해줘
df[df['height'] >= 180]['name']
# df['name'][df['height'] >= 180] 순서가 바뀌어도 상관없음 (전자 권장)
# 키가 180 이상인 사람 중에서 이름, 성별, 키만 출력해줘
df[df['height'] >= 180][['name','gender','height']]
# 리스트 안에 컬럼들을 넣어야 함 (대괄호 개수 주의)
# 키가 180 이상인 사람 중에서 이름, 성별, 키만 출력해줘 (loc 사용)
df.loc[df['height'] >= 180, ['name','gender','height']]
8. 포함된 값 확인하기
| isin() | 리스트 안에 포함된 값인지 확인하는 메서드 |
# 새로운 변수 blood에 'A', 'B'를 넣고 blood 안에 해당하는 것만 가져와
blood = ['A', 'B']
df['blood'].isin(blood)
# 혈액형이 A, B인 사람의 데이터 가져와
df[df['blood'].isin(blood)] # df.loc[df['blood'].isin(blood), :]
# loc와 iloc 차이점
- 행과 열에 대한 조건에 슬라이싱 사용 시 끝점 포함 여부
- 끝 점 미포함 : 파이썬 기본 슬라이싱(list, tuple, str), Numpy 슬라이싱, pandas iloc 슬라이싱
- 끝 점 포함 : pandas loc
- 이런 방식을 택한 이유 :
- 문자열/날짜 레이블일 때 끝점 미포함 방식이 불편함
- 직관적으로 '이 날짜' 까지 표현하는 것이 편함
- 슬라이싱이라는 개념 자체는 "범위로 데이터를 잘라낸다" 이기 때문에 둘 다 슬라이싱이 맞지만
명확한 구분을 위해 아래와 같이 표현하면 구분이 쉽다
레이블 기반 슬라이싱(loc)
위치 기반 슬라이싱 (iloc)
9. 결측값 Missing Value
- 데이터가 존재하지 않거나 비어있는 값
- 보통 NaN(Not a Number) 형태로 표현
- 데이터 분석과 머신러닝에서는 결측값이 매우 중요
통계 분석, 모델 학습 과정에서 오류나 성능 저하 발생 가능성
-> 결측값 처리는 데이터 품질을 높이고 정확한 분석 결과를 얻기 위해 매우 중요한 전처리 과정 - 결측값 처리 방법은 여러 가지가 있는데 평균, 중앙값 등 대체 값으로 처리
| 메서드 | 설명 |
| isna() | - NaN 값을 가진 데이터 전체 출력 - null 아닌 값이 있으면 False, null 값이면 True |
| isnull() | (위와 동일) |
| notna() | -NaN 값을 가지지 않은 데이터 전체 출력 - null 아닌 값이 있으면 True, null 값이면 False |
| notnull() | (위와 동일) |
| fillna() | - 결측값을 채워주는 함수 - 복사해서 사용하기 때문에 원본을 수정하는 것이 아님 |
| dropna() | - 결측값이 있는 행 또는 열 제거. 한개라도 있는 경우 삭제 - axis = 0 (행 삭제) -> 기본값 - axis = 1 (열 삭제) |
# 'height'열에서 NaN가 있는지 검사
df['height'].isna()
# 'height'열에서 NaN 값이 있는 데이터만 가져와
df[df['height'].isna()]
# 넘파이 사용하기 위해 import
import numpy as np
# 8, 19행 그리고 height 열에 데이터를 NaN으로 저장해
df.loc[[8,19], 'height'] = np.nan # 한 번에 저장
# df.loc[8, 'height'] = np.nan # 하나씩 저장
# df.loc[19, 'height'] = np.nan # 하나씩 저장
# height 열이 NaN인 데이터만 가져와
df[df['height'].isna()]
# height 열이 NaN이 아닌 데이터만 가져와
df[df['height'].notnull()]
df[~df['height'].isna()]
# height열이 NaN인 데이터 중 name만 가져와
df[df['height'].isna()]['name']
# height가 NaN이 아닌 데이터의 name, company, gender 데이터를 가져와
df[df['height'].notnull()][['name','company','gender']]
df.loc[df['height'].notnull(), ['name', 'company', 'gender']]
# df를 df_copy로 복사해오자
df_copy = df.copy()
df_copy
# df_copy의 'height'열을 출력해보자
df_copy['height']
# 위 결과의 결측값을 0으로 채워보자
df_copy['height'].fillna(0)
# 원본은 그대로 있는 것 확인 가능
df_copy['height']
10. 평균값과 중앙값
| 구분 | 메서드 | 설명 |
| 평균(Mean) | mean() | - 데이터의 모든 값을 더한 뒤 데이터 개수로 나눈 값 - 전체 데이터의 중심적인 경향을 나타내는 대표적인 통계값 |
| 중앙값(Median) | median() | - 데이터를 크기 순으로 정렬했을 때 가장 가운데 위치한 값 - 이상치(Outlier)의 영향을 적게 받음 - 데이터 개수가 짝수라 가운데 값이 2개라면, 두 가운데 값의 평균을 중앙값으로 사용 |
- 결측값을 키의 평균, 중앙값으로 채워보자
height = df_copy['height'].mean() # 먼저 평균을 구해서 변수에 저장
df_copy['height'] = df_copy['height'].fillna(height) # 평균으로 결측값을 채운 후 덮어서 저장
df_copy['height'] # 평균으로 결측값이 채워진 것을 확인 가능
height = df_copy['height'].median() # 먼저 중앙값을 구해서 변수에 저장
df_copy['height'] = df_copy['height'].fillna(height) # 평균으로 결측값을 채운 후 덮어서 저장
df_copy['height'] # 평균으로 결측값이 채워진 것을 확인 가능
# df를 df_copy로 복사해오자
df_copy = df.copy()
df_copy
# 결측값 있는 행 삭제
df_copy.dropna()
# 결측값 있는 열 삭제
df_copy.dropna(axis=1)
11. 행, 열 추가 및 삭제
| drop() | - 괄호 안에 삭제할 행 또는 열 입력 - axis = 0 : 행 - axis = 1 : 열 |
- 행 추가
- 새로운 딕셔너리 생성
- loc 사용하여 df_copy 맨 마지막 인덱스에 행 추가⭐
- 잘 반영되었는지 확인
dic = {
'name': '김사과',
'company': '애플',
'gender': '여자',
'birthday': '2000-01-01',
'height': 160.0,
'blood': 'A',
'brand': 1234567
}
df_copy.loc[len(df_copy)] = dic # ⭐
df_copy
- 열 추가
- 모든 행에 대해 '대한민국' 라는 'nation' 컬럼 추가
- 잘 반영되었는지 상위, 하위 5개 확인
- 김사과의 nation을 '미국'으로 수정
- 잘 반영되었는지 하위 5개 확인
df_copy['nation'] = '대한민국'
df_copy.head()
df_copy.tail()
df_copy.loc[df_copy['name'] == '김사과', 'nation'] = '미국'
df_copy.tail()
- 행 삭제
- 인덱스 20인 행 삭제
- 인덱스 1, 3, 5, 7, 20인 행 삭제
df_copy.drop(20, axis=0) # 0: 행, 1: 열
df_copy.tail()
df_copy.drop([1, 3, 5, 7, 20], axis=0)
- 열 삭제
- 'nation' 열 삭제
- 'nation', 'company' 열 삭제
12. 통계 함수
- df_copy데이터의 height를 가져와서 합계, 개수, 평균, 중앙값, 최대값, 최소값, 분산, 표준편차 출력
print(df_copy['height'].sum()) # 합계
print(df_copy['height'].count()) # 개수, NaN은 포함하지 않음
print(df_copy['height'].mean()) # 평균
print(df_copy['height'].median()) # 중앙값
print(df_copy['height'].max()) # 최대값
print(df_copy['height'].min()) # 최소값
print(df_copy['height'].var()) # 분산
print(df_copy['height'].std()) # 표준편차
13. 분산과 표준편차

- 분산 :
- 데이터가 평균으로부터 얼마나 퍼져 있는지를 수치로 나타낸 값
- 값이 클수록 데이터의 흩어짐이 크다는 의미
- 값이 작을수록 데이터가 평균 근처에 모여 있다는 의미
- 단점 : 차이를 제곱해서 계산하기 때문에 단위가 원래 데이터와 달라짐
- 표준편차 :
- 분산에 제곱근(sqrt)를 취한 값
- 분산의 단점을 해결하기 위해 사용
- 직관적 해석 가능
13. 그룹화하기
| groupby() | 데이터를 그룹으로 묶어 분석할 때 사용 |
| numeric_only=True | 계산해 낼 수 있는 수치 데이터만 |
- 혈액형으로 그룹화하여 출력
- 혈액형으로 그룹화한 후 개수 출력
- 혈액형으로 그룹화한 후 계산할 수 있는 데이터만 평균 출력
- 혈액형으로 그룹화한 후 계산할 수 있는 데이터만 합계 출력
- 혈액형과 성별로 그룹화한 후 계산할 수 있는 데이터만 평균 출력
# 그룹 객체(주소 출력)가 출력되는 것 확인 가능
df_copy.groupby('blood') # <pandas.api.typing.DataFrameGroupBy object at 0x000002A148D8FBC0>
# 그룹을 맺어 통계함수 사용 (NaN은 값으로 치지 않음)
df_copy.groupby('blood').count()
df_copy.groupby('blood').mean(numeric_only=True)
df_copy.gourpby('blood').sum(numeric_only=True)
#
df_copy.groupby(['blood','gender']).mean(numeric_only=True)
2.
- 내용
* CSV 파일이랑 Excel 파일은 같다? False
CSV 파일은 엑셀에서도 열리고 메모장에서도 열리는 아주 가벼운 파일
* isna()와 isnull()이 완전 같은 의미인데 둘 다 있는 이유 :
- isnull은 R언어의 영향을 받아 원래 있던 메서드인데
- na(Not Available) 개념을 더 명확히 표현하기위해 isna가 이후에 추가됨
- (pandas의 다른 메서드들과 네이밍을 맞추기 위함)
'KDT > 1. Python' 카테고리의 다른 글
| [9일차-2] 넘파이, 판다스 (0) | 2026.06.05 |
|---|---|
| [9일차-1] 모듈 (0) | 2026.06.01 |
| [8일차] 객체지향 프로그래밍(다형성), 예외, 매직메서드 (0) | 2026.05.29 |
| [7일차] 사용자정의함수, 객체지향프로그래밍(캡슐화, 상속) (0) | 2026.05.28 |
| [6일차] 제어문(반복문), 사용자 정의 함수(함수 생성, 전역변수, 지역변수) (0) | 2026.05.27 |