DataFrame
: 2차원 구조 (2개 이상의 Series로 구성됨)
- axis = 0 : index 방향
- axis = 1 : column 방향
Dictionary → DataFrame
member = {
'Attack' : [111, 222, 333],
'Defence' : [444, 555, 666],
'Luck' : [777, 888, 999]
}
member_df = pd.DataFrame(member)
member_df
- key → column명
- value → 각각의 row를 이룸
DataFrame - Columns
: 인덱스 혹은 배열과 같은 것
- 데이터가 제공되면 그것으로 컬럼명이 설정된다.
- 제공되지 않으면 디폴트는 RangeIndex(0, 1, 2, ...n) 로 설정된다.
## 리스트로 동일한 데이터 프레임을 만든다면 1
data = [[111, 222, 333], [444, 555, 666], [777, 888, 999]]
columns = ['Attack', 'Defence', 'Luck']
member_df = pd.DataFrame(data=data, columns=columns)
member_df
## 리스트로 동일한 데이터 프레임을 만든다면 2
data = [[111, 444, 777], [222, 555, 888], [333, 666, 999]]
columns = ['Attack', 'Defence', 'Luck']
member_df = pd.DataFrame(data=data, columns=columns)
member_df
▶ Series에서 데이터가 들어갈 때, 하나의 값이 하나의 '객체'로서 작용한다.
즉, 위의 예제 (2)에서 [111, 444, 777]이 하나의 '객체'여서 0번 인덱스로 묶여있는 것이다.
'객체' 단위로 저장되기 때문에 하나의 행에 [111, 444, 777]이 순서대로 들어가는 것이다.
▶ 위의 예제 (1)에서 딕셔너리를 DataFrame으로 만들 때는 key가 컬럼명이기 때문에,
`'Attack' : [111, 222, 333]`이 하나의 Series로 들어가는 것이다.
따라서 List → DataFrame과 Dictionary → DataFrame 만드는 과정에는 차이가 있다.
리스트를 데이터 프레임으로 만들 때 `columns`를 할당해주지 않으면 RangeIndex가 자동으로 부여된다.
## 리스트로 동일한 데이터 프레임을 만들 때 컬럼을 정해주지 않으면
data = [[111, 444, 777], [222, 555, 888], [333, 666, 999]]
columns = ['Attack', 'Defence', 'Luck']
member_df = pd.DataFrame(data=data)
member_df
DataFrame - Index
: 인덱스 (행)
- 데이터가 제공되면 그것으로 인덱스명이 설정된다.
- 제공되지 않으면 디폴트는 RangeIndex(0, 1, 2, ...n) 로 설정된다.
data = [[111, 444, 777], [222, 555, 888], [333, 666, 999]]
columns = ['Attack', 'Defence', 'Luck']
index = ['Spencer', 'Tommy', 'Uriel']
member_df = pd.DataFrame(data=data, columns=columns, index=index)
member_df
.shape
: 행렬의 크기(모양)을 알 수 있다.
member_df.shape
(3, 3)
Q. 만일 컬럼명이 shape라면 발생할 수 있는 문제는?
A. 우선순위는 `.shape` 속성의 호출에 있기 때문에 컬럼명을 shape라고 지정해도 컬럼에 해당하는 시리즈가 조회되는 것이 아니라
행렬의 크기를 보여준다. 컬럼명을 info와 index로 설정해도 마찬가지이다.
csv로 DataFrame 생성
- 단일 열은 Series로 접근
- 다중 열은 DataFrame으로 접근
DataFrame의 속성(Attr, attr, attributes)
.shape
: 데이터프레임의 구조를 알 수 있다.
.info()
: 데이터프레임의 정보를 알려준다. 메소드이기 때문에 소괄호 필요.
.size
: 데이터프레임의 크기를 알 수 있다.
.values
: values만 모아서 값들을 확인할 수 있다.
.dtypes
: 각 컬럼에 해당되는 값들의 데이터 타입을 Series형태로 보여준다.
- 인덱스 객체 : 컬럼명
- value : 데이터타입
.index
: 인덱스가 어떤 객체로 이루엉져 있는지 알 수 있다.
.columns
: 컬럼이 어떤 인덱스 객체로 이루어져 있는지 알 수 있다.
.axes
: 각 축의 인덱스가 어떤 식으로 구성되어 있는지 인덱스 정보와 열 정보를 알 수 있다.
메소드(Method)
.sum()
: 각 열(=각 Series)를 합산한 결과로 또 다른 Series를 만든다.
- Series는 `.sum()`하면 단일 값
- DataFrame을 `.sum()`하면 Series
- 위아래로 합산 : axis = 0 또는 axis='index' ← 디폴트
- 좌우로 합산 : axis = 1 또는 axis='columns'
.max()
.min()
.mean()
: 평균
.median()
: 중앙값
.prod()
: 곱셈 연산
.count()
: 비결측치의 개수(=NaN 데이터는 제외하고 카운트)
.describe()
: 요약 통계량
.mode()
: 최빈값
Kaggle 실습
🔗 실습 링크 : https://www.kaggle.com/datasets/harshitshankhdhar/imdb-dataset-of-top-1000-movies-and-tv-shows
열 순서 변경하기
# 판다스 모듈 불러오기
import pandas as pd
# 열 순서 변경하기
cols = ['Released_Year', 'Genre', 'Series_Title', 'Director', 'Meta_score', 'IMDB_Rating', 'No_of_Votes', 'Certificate', 'Gross']
# 열 지정해서 데이터 불러오기 -> 변경된 순서 반영 안됨
movie_df = pd.read_csv('imdb_top_1000.csv', usecols=cols)
movie_df
변경된 열 순서를 반영하기 위해서는 DataFrame 조회시, 대괄호 안에 변경된 열 순서를 담은 리스트를 넣어야 한다.
# 열 순서 변경하기
cols = ['Released_Year', 'Genre', 'Series_Title', 'Director', 'Meta_score', 'IMDB_Rating', 'No_of_Votes', 'Certificate', 'Gross']
# 변경된 순서 반영
movie_df[cols]
.info()
: 데이터프레임의 간단한 요약 정보를 출력해준다.
- index dtype
- columns
- non-null values
- memory usage
# 실습 데이터의 구성 파악
movie_df.info()
`1000 entries` : 정상적이라면 1000개의 객체(데이터)가 있어야 함
`Non-Null Count` : 결측치가 아닌 값의 개수
→ 예제에서는 'Meta_score', 'Certificate', 'Gross' 컬럼에서 결측치가 존재
.describe()
: 집계함수 요약
# 유효숫자 1자리 = 소수점 둘째자리에서 반올림
movie_df.describe().round(1)
🤔 문제
Gross(수익) 컬럼도 숫자로 이루어져있길래 `.describe()` 에 나타날 줄 알았지만 없었다.
컬럼값을 자세히 보면 콤마(,)로 이루어져 있는 문자열(object) 데이터 타입이다.
💡 해결방법
1. `.str.replace()` : Gross 컬럼값의 포맷 변경(콤마 제거)
- `.str` : 먼저 문자 타입으로 변환해준다.
- `.replace('대체전문자열', '대체후문자열')` : 원하는 문자열을 대체하고 싶은 문자열로 변환해준다.
# 1단계. comma 제거해서 원본에 반영
movie_df['Gross'] = movie_df['Gross'].str.replace(',', '')
2. `.fillna()`: Gross 컬럼값 중 결측치 대체하기 (0으로)
- `.fillna(값)` : 결측치를 원하는 값으로 대체한다.
# 'Gross'컬럼의 결측치를 0으로 대체
movie_df['Gross'] = movie_df['Gross'].fillna(0)
3. `.astype()` : dtype 데이터 타입 [문자] → [숫자] 변환하기
- `.astype(원하는데이터타입)` : 원하는 데이터타입으로 변환해준다.
# .astype('변환형')
movie_df['Gross'] = movie_df['Gross'].astype('int64')
#확인
movie_df.info()
▶ Gross 컬럼값의 결측치가 0이고 (`1000 non-null`) 데이터 타입이 숫자형(`int64`)임을 확인했다.
다시 `.describe()`로 집계함수 요약을 출력해보자.
# .describe()로 요약하면
movie_df.describe().round(1)
집계함수의 인자
percentiles =
: `.describe()`의 파라미터로, 각각의 00% 백분위수를 확인할 수 있다.
- 필요하다면 퍼센트 범위를 지정할 수도 있다.
# [20%, 40%, 60%, 80%] -> [.2, .4, .6, .8]
movie_df.describe(percentiles=[.2, .4, .6, .8])
- 빈 리스트를 넣어도 50%는 기본적으로 남는다.
(→ 50%는 중앙값(median)이기 때문에 percentiles 처리에 오류가 있다는 오해가 종종 있다.)
numeric_only =
: `.max()`, `.min()` 등 집계함수의 파라미터로, 숫자형 데이터만 있는 건 아니기 때문에 숫자만 처리하도록 지정
movie_df.min(numeric_only=True)
include =
- `all` : 입력된 모든 열이 결과에 포함되도록 설정
- list-like of dtypes(데이터 유형의 목록 형태) : 결과를 지정된 데이터 유형으로 제한
- None : 결과에는 모든 숫자 열이 포함 ← 디폴트
- `['0']` : 문자열 열만 표현
- `['int64']` : 정수형 열만 표현
- `['float64']` : 소수형 열만 표현
- `['number']` : 숫자형 열만 표현
- `numpy.number` : 숫자 유형에 대한 결과를 제한
- `numpy.object` : 객체 열에 대한 결과를 제한
- `category` : pandas 범주형 열을 선택
표현할 수 없는 항목은 `NaN`으로 나온다.
- 문자열 데이터 - mean, std, min 등 표현 불가
- 숫자형 데이터(연속형) - unique, top, freq 등 표현 불가
.quantile()
: 분위수(오름차순/내림차순 정렬 되어있을 때 이를 나누는 지점)를 반환하는 집계함수
- `q` : 분위 지점 (디폴트 = 0.5)
- `axis` : 축 (디폴트 = 0)
- `numeric_only` : 숫자형 데이터만 처리하는지 여부 (디폴트 = False)
- `interpolation` :
- `method` :
- percentiles : 백분위수(%), 100개 영역 지점이 존재
- quartiles : 4분위수, 4구역 지점이 존재
정렬
1) .nlargest()
: 큰 값부터 정렬 (비파괴적 처리)
- n : 몇 개를 반환할 건지
- columns : 어떤 컬럼을 기준으로 정렬할건지
- keep : 중복값이 있을때 어떤 방식으로 보여줄 것인지
- `first` : 첫 번째로 발견된 것을 먼저 보여줌 ← 디폴트
- `last` : 맨 마지막에 발견된 것을 먼저 보여줌
- `all` : 중복값이 발생하더라도 삭제하지 않음(이 경우, n보다 많은 항목이 선택될 수 있음)
▶ `.nlargest()`와 `.sort_values(ascending=False)`는 동일한 기능을 수행하지만,
내림차순에 있어서 성능은 `.nlargest()`메소드가 더 좋다.
2) .nsmallest()
: 작은 값부터 정렬 (비파괴적 처리)
▶ `.nlargest()`와 `.nsmallest()`는 모두 비파괴적 처리이지만, `.sort_values()`와 달리 `inplace` 파라미터가 없다.
즉, 원본을 변경하고 싶다면 덮어쓰기를 해야한다.
3) .sort_values
- `Series.sort_values()`보다 `DataFrame.sort_values()` 인자가 `by`가 하나 더 있따.
- Series는 단일 열이 있지만, DataFrame은 다중 열이기 때문에 정렬하고자 하는 열 정보가 필요하다.
인덱스 제어
1) .set_index()
: 특정 열을 인덱스로 설정(비파괴적 처리)
- 인덱스 열로 지정되면서 원래 사용하던 해당 열이 삭제되었다. (move처럼 자리를 이동한 것)
- inplace : 비파괴적 처리 여부
- drop : `.set_index()`로 인덱스 열을 변경할 때, 인덱스로 지정된 열을 기존 열에서 삭제할지 여부 결정
- `True` : 삭제 ← 디폴트
- `False` : 유지
★ df.index.name =
: 데이터프레임의 인덱스명을 변경
2) .reset_index()
: 인덱스를 초기화 (`df.index.name`처럼 직접 속성에 접근해서 변경한 인덱스명은 유지됨)
- drop : 기존 인덱스를 데이터 열로 삽입되지 않고 삭제됨 (기본 정수형 인덱스로 재설정)
- `True` : 삭제
- `False` : 유지 ← 디폴트
3) index_col =
: 데이터를 읽어올 때 인덱스 열 설정 (`read_csv()`에서의 파라미터)
행렬삭제 .drop
columns=
: 열 삭제
# 열 2개 삭제해서 원본 반영
movie_df.drop(columns=['Poster_Link', 'Overview'], inplace=True)
movie_df.head()
axis=1
: 열 삭제
#columns 방향으로 삭제, 원본 반영
movie_df.drop(['Star1', 'Star2', 'Star3', 'Star4'], axis=1, inplace=True)
movie_df.head()
labels=
: IndexLabel로 행 삭제
# indexlabel로 행삭제해서 원본 반영
movie_df.drop(labels=1, inplace=True)
movie_df.head()
list-like
# 삭제할 인덱스레이블을 리스트로 전달
movie_df.drop(labels=[2, 3, 4], inplace=True)
# movie_df.drop([2, 3, 4], inplace = True) 로 써도 됨
movie_df.head()
errors=
: `.drop()` 에러 제어 파라미터
- `'ignore'` : 데이터를 삭제할 때, 없는 데이터를 삭제해야 하는 상황이어도 무시하고 넘어감
- `'raise'` : 데이터를 삭제할 때, 없는 데이터를 삭제해야 하는 상황이면 에러 발생 ← 디폴트
# 이미 삭제한 데이터이지만 에러 발생 안함
movie_df.drop([2, 3, 4], inplace=True, errors='ignore')
movie_df.head()
del을 이용한 열 삭제
- 해당 데이터의 주소로 찾아가서 del로 그 할당을 삭제
- del은 리스트 전용 키워드로, 딕셔너리 전용 삭제 키워드가 아니다.
# 'Runtime' 을 삭제한다고 할 때
del movie_df['Runtime']
movie_df.head()
→ 따라서 이미 삭제된 데이터를 삭제하려고 할 때, 해당 데이터의 주소가 삭제된 상태이기 때문에 `KeyError`가 발생한다.
# 이미 삭제된 열을 삭제하려고 하면 에러 발생
del movie_df['Runtime']
→ `del`로 삭제하려고 할 때 오류 제어를 하려면, `try~except~finally`문으로 예외 처리를 해야한다.
열 조회
단일열 조회를 위해 `df['컬럼명']`을 하거나, 다중 열 조회를 위해 `df[['컬럼명1', '컬럼명2', ...]]`를 할 때는
없는 열을 조회할 경우 에러가 발생한다.
이 경우, 좀 더 안전하게 데이터를 가져오려면 Python의 Dictionary처럼 `.get()` 을 이용하기도 한다.
.get()
- 없는 열을 조회해도 None이 반환된다.(=아무것도 출력되지 않음)
- `.get()`으로 조회하는 것은 값 반환(return value)이라서 수정이 불가능하다.
df['컬럼명'], df[['컬럼명1', '컬럼명2', ...]]
- 없는 열을 조회하면 `KeyError`가 발생
- `[]`로 접근하는 것은 주소 접근(address access)이라서 수정이 가능하다.
열 추가
1) 일련화 된 값 추가
- Dictionary 처럼 `df['새컬럼명'] = 일련화된 값` 으로 값을 추가할 수 있다.
- List의 `.append()` 처럼 위치는 지정할 수 없고 전부 맨 뒷쪽 열에 붙는다.
2) .insert()를 이용한 일련화된 값 추가
- `.insert()`를 이용하면 위치 지정이 가능하다.
- `loc`: 삽입될 위치 지정
- `column`: 삽입될 열의 이름
- `value`: 삽입될 열의 값(Series or array-like : 개수만 맞으면 추가 가능)
- `allow_duplicates` : 중복 허용 여부
- False : 중복 불가 ← 디폴트
- True : 중복 허용
Q. 만약 데이터 개수가 맞지 않을 경우 삽입하면 어떻게 될까?
1) `df['새컬럼명'] = 값` 로 열 추가할 경우
: 에러가 발생한다.
2) `.insert(value = 시리즈)`로 열 추가할 경우
3) `df['새컬럼명'] = 시리즈` 로 열 추가할 경우
: 에러가 발생하지 않는다.
- 일치된 인덱스에 맞춰서 값이 추가된다.
- 일치되지 않은 행은 `NaN`으로 대체된다.
→ 이런식으로 데이터 전처리를 하다보면 `NaN` 데이터가 쉽게 생겨나기도 한다.
결측치 제어
.dropna()
: 모든 행x열의 공간에서, NaN이 1개라도 있으면 행(디폴트) 삭제
- `axis` : 축
- `0` : 행 방향 ← 디폴트
- `1` : 열 방향
- `how` : NA 발견시 제거되는 방식
- `'any'` : 행의 cell 중 1개라도 NA이면 해당 행 삭제 ← 디폴트
- `'all'` : 행의 모든 cell이 NA여야 해당 행 삭제
- `subset` : 특정 열의 NA만 확인
.fillna()
: NaN을 원하는 값으로 대체
- `value` : 원하는 값 지정 (올 수 있는 값 : Hashable | Mapping | Series | DataFrame)
- `method` : 대체 방식 지정
- `ffill` : (`axis=0`일 때) 앞 행의 값과 동일한 값으로 채움 → 데이터의 연속성 유지
- `backfill`/ `bfill` : (`axis=0`일 때)뒷 행의 값과 동일한 값으로 채움
Pandas 2. 1. 0부터는 `backfill` 사용하지 않음 - None : ← 디폴트
- 🤔 문제점 : 연속된 결측치가 나올 경우 성능이 떨어진다.
연속된 결측치가 너무 많으면 전체 집계를 했을 때, 오차 범위가 커지기 때문이다.
- `limit` : 연속된 결측치가 나올 결우, 결측치 대체 횟수 지정
- `int` : 지정한 정수값 횟수 만큼 결측치 대체
- None : ← 디폴트
'데이터 분석 Data Analytics > 프로그래머스 데이터분석 데브코스 2기' 카테고리의 다른 글
[TIL] 데이터분석 데브코스 29일차 - DataFrame 심화 : (0) | 2024.04.02 |
---|---|
[TIL] 데이터분석 데브코스 28일차 (2) - DataFrame 활용 : (1) | 2024.03.30 |
[TIL] 데이터분석 데브코스 27일차 - Anaconda/Series/ (0) | 2024.03.26 |
[TIL] 데이터분석 데브코스 26일차 - Pandas/ (0) | 2024.03.25 |
[TIL] 데이터분석 데브코스 25일차 - 트랜잭션/고급SQL문법/과제리뷰 (1) | 2024.03.23 |