21. pandas 추가 – DataFrame 데이터 변형(중복행 제거/ 매핑/ 치환/ 카테고리 자료형)
중복된 행 제거하기
df = pd.DataFrame({'k1': ['one'] * 3 + ['two'] * 4,
'k2': [1, 1, 2, 3, 3, 4, 4]})
위와 같이 딕셔너리형태의 value값에 [ 성분 ] * n 또는 직접 입력하여, 위<->아래 행이 모두 중복된 성분을 가지는 행이, 여러개 있는 DataFrame이 있다.
- df.duplicated()함수를 이용해, 성분이 중복되는 행이 있을 때, 아래 행에다가 True를 나타내는 Boolean 마스크를 뽑을 수 있다.
이 것을, 마스크로 인덱싱하여 중복된 행을 제거할 수 있지지만 - df.drop_duplicates()를 사용하여, 중복된 행들이 제거되고 unique한 행들만 얻을 수 있다
이 때, 새로운 열(0~6까지를 성분으로 가지는 )을 추가해서, 완전히 중복되는 행이 없도록 만들어보자.
df["v1"] = np.arange(7)
완전히 중복된 행이 없는 상태에서, drop_duplicates( )함수에 인자로서, 중복검사의 기준이 되는 열을 지정할 수 있다.
이 때, 중복된 행을 제거하는 과정에서 아무 인자도 안주면, 중복 되는 것 중 가장 첫번째 껏만 남긴다.
- df.drop_duplicates( ["k1" ] ) 는 k1열만 기준으로 중복된 행을 제거한다.
- df.drop_duplicates( ["k1", "k2"] , keep="last" )을 통해 k1열과 k2열을 기준으로 모두 중복된 행을 제거하는데,
keep인자에 last를 통해, 중복 행 중, 가장 마지막행만 남길 수 있다.
(default 혹은 keep = “first”는 가장 첫 행만 남긴다,
데이터 매핑(Mapping) with 딕셔너리
df의 특정 열이, 유한한 값 중 1가지를 가진 상태에서, 단순화를 위해 더 작은 가짓수의 값들 중 하나로 매핑(mapping)하고 싶을 수 있다.
예를 들어, 특정열이 7가지의 성분을 가지는데, --- > 중간 딕셔너리(7to3)를 만들어 사용자정의함수로 ---> 3가지 성분을 가진 새로운 열로 범위를 좁힐 수 있다.
df2 = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami',
'corned beef', 'Bacon', 'pastrami', 'honey ham',
'nova lox'],
'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
food라는 열을 더 작은 가지수로 매핑하기 위해 다음과 같은 딕셔너리를 생성한다.
food열의 unique한 성분(소문자)이 키값이 되고 : 더 작은 범위로 매핑시킬 값들이 value가 된다.
meat_to_animal = {
'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'
}
사용자 정의 함수인 .apply( lambda x : ~ )를 이용해서
food열의 값들이 -->meat_to_animal에 –> pig, cow, salmon 중 하나의 값으로 치환시킨 뒤, 새로운 열로 추가해보자.
(13강에서는 apply()함수의 인자에 들어갈 func = lambda x: x.max() – x.min() 으로 미리 함수를 정의해놓고 집어 넣었다.
df. apply ( func , axis = 0 ) 을 통해 df의 각각의 열에 대해, 성분의 최대값-최소값을 계산해서 각 열별로 Seriess형태로 나오게 했음)
df2의 food열에 대해서 .apply()함수를 적용시켜야 한다. 각 성분들이 lambda의 x로 들어갈 것이다.
( 특정 열에 apply( lambda x: )를 적용하면, 열의 각 성분들이 for문 처럼 모든 성분들이 대입되어 돌아간다.)
- df2["animal"] = df2["food"].apply(lambda x: meat_to_animal[x.lower()]) 를 호출하게 되면
(1) df2에는 bacon도 있고, Bacon도 있으니, 먼저 food열의 각 성분을 의미하는 x에 .lower()함수를 통해 다 소문자로 바꾼다.
(2) food열의 각 성분(소문자)을 의미하는 x.lower()를 meat_to_animal의 열인덱싱 시켜,
딕셔너리에 있는 키 값들 중 하나이므로, 해당하는 value값인 pig or cow or salmon 중 하나가 튀어나올 것이다.
(3) 각 food열의 성분들에 대해, 딕셔너리의 열인덱싱을 통해서, 더 작은 범위의 성분들이 각각 나온 것을
새로운 열인 df2[“animal” ] 에 하나씩 담는다.
값 치환하기 with replace()함수 in Series
이전 12강에서 결측값 NaN을 처리하기 위해 다른값으로 치환 한 적이 있다.(df.dropna / df.fillna(value=)/df.isnull() [“전체기준열”]로 마스크생성)
pandas의 Series에서는 .replace()라는 함수를 제공하여, 손쉽게 특정숫자를 치환할 수 있다.
s = pd.Series([1., -999., 2., -999., -1000., 3.])
위 Series에서 몇몇 값들은 많이 벗어난 이상치(outlier)를 확인할 수 있다.
- 이 이상치를 NaN으로 치환해보자.
s2 = s.replace(-999, np.nan) - NaN도 다른 값으로 치환할 수 있다.
s3 = s2.replace(np.nan, 0)
하지만, NaN을 치환할 때는, fillna( 값 ) 을 통해 더 간편하다.
s2.fillna(0)
카테고리 자료형(범주형 데이터) 생성, 추가, 대소관계
지금까지의 dataset에서 유한가지 값들 중 한 가지를 성분으로 하는 특정열을 많이 보았다.
예를 들어, 성별을 나타내는 열에서는 male/female 를 가지는 경우가 대표적이다. 이것을 범주형 or 카테고리형 데이터라고 한다.
pandas에서는 Categories라는 특별한 형태의 자료형을 제공한다. df의 특정열에서 본 것과 유사하게, Series로 제공되는 자료형이다.
단순하게 문자열로만 저장되어있는 자료형을, 카테고리형(범주형)으로 바꾸게 되면, 메모리 양을 획기적으로 줄이면서 동시에 이에 대한 각종 통계 분석도 간편해진다.
df3 = pd.DataFrame({"id":[1,2,3,4,5,6], "raw_grade":['a', 'b', 'b', 'a', 'a', 'e']})
df3 가운데 하나의 열을 카테고리 자료형으로 변환해보자.
raw_grade열은 a,b,e중 하나의 값을 가질 수 있는데, 단순히 문자열로 저장되어있다. 이를 카테고리 자료형으로 치환해보자.
- 카테고리 자료형으로 만들고 싶은 열에 .astype()함수를 적용하고, 그 type을 “category”로 주어 새로운 열로 추가해보자.
df3["grade"] = df3["raw_grade"].astype("category")
열 인덱싱을 통해서, 카테고리 자료형의 Series라는 것을 확인해보자.
a, b, e 3가지 카테고리 자료형을 가질 수 있는 것을 확인할 수 있다. - 카테고리의 값을 확인 하는 방법은, 열인덱싱.cat.categories 를 입력하면 된다.
df3["grade"].cat.categories - 카테고리의 기존값을 다른이름으로 새롭게 정의해 줄 수 있다. cat.categories로 확인된, 각 순서대로 대응되어서 들어간다.
df3["grade"].cat.categories = ["very good", "good", "very bad"]
(my : cat.categories는 카테고리 자료형 특정열을 확인 + 이름바꾸기가 가능하구나.) - 카테고리의 값을 추가해 줄 수 도 있다. cat.set_categories () 함수에 리스트 형식으로 인자를 넣어줘야한다.
이 때, 인자에는 기존 카테고리의 값도 포함시켜서 리스트형식으로 넣어주면, 기존 값은 유지하면서
해당 열을 확인해보면, 카테고리의 값이 늘어나는 것을 확인할 수 있다.
df3["grade"] = df3["grade"].cat.set_categories( ["very bad", "bad", "medium", "good", "very good" ])
( 카테고리열인덱싱.cat.categories 로 확인하는 것이 정상?)
카테고리의 또다른 특징은, cat.set_categories()의 인자에 넣어준 리스트의 순서대로, 대소관계를 가진다는 것이다.
이것은 sort_values(by=”열”)로 확인해보면, 오름차순 정렬 될 때, 1번째 very bad > 4번째 good> 5번째 very good 순서로 정렬이 된다. 이말은 마지막에 넣어준 카테고리값이 가장 크다는 것을 의미한다.
이러한 카테고르의 대소관계의 특징은, 사원 > 대리> 과장> 부장> 사장 같은 자료형에 대해서 효과적으로 정렬할 수 있다.
숫자 데이터의 카테고리화
연령을 나타내는 열이 숫자로 구성되어 있으며, 일정 나이 기준으로 청년, 성인, 노년 처럼 구간을 나눈 뒤, 관리하면 편해진다.
조건문을 활용해서 나눌 수 도 있지만, pandas에는 빠르게 숫자데이터를 카테고리화 하는 기능을 가지고 있다.
2개의 파이썬 리스트에서 ages 는 카테고리화 할 숫자데이터이며, bins는 각 구간을 나눠줄 숫자값이다.
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
18~25 / 25~35 / 35~60 / 60~100 이렇게 총 4구간이 존재하게 될 것이다.
pandas에서 제공하는 pd.cut( , )함수는 인자로 (카테고리화 할 숫자데이터, 짜를 구간의 구분값)를 넣어 쉽게 카테고리화 할 수 있다.
이렇게 cut(,)함수로 잘린 데이터는 카테고리 자료형 Series로 반환되게 된다.
- cats = pd.cut(ages,bins) 를 통해 자른 뒤, cats라는 곳에 옮겨 담는다.
확인해보면, ages가 5개의 구간 분값에 의해 4구간의 카테고리 자료형으로 반환된다.
cats = pd.cut(ages,bins) - cats.codes 를 통해, ages의 각 성분이 몇번째 구간에 속해있는지 정수index처럼 표시되는 것을 알 수 있다.
cats.codes
예를 들어, 20이라는 것은 0=첫번째 구간에, 27은 1=두번째 구간에 속한다는 것을 알 수 있다. - cats.value_counts() 를 통해서, 값x 각 구간에 따른 성분의 갯수를 확인할 수 있다.
cats.value_counts()
(my : 특정열.value_counts()는 특정열 성분에 따라 포함되는 행수를 파악하는데 쓰였지만,
카테고리 자료형(Series)에서는, 각 구간에 속한 성분의 갯수도 파악할 수 있다)
pd.cut(, ) 함수를 실행할 때, bins에 준 각 구간 구분값이 자동으로 카테고리명( (18,25] )을 결정하는 것을 알 수 있다.
이 때, pd.cut(,)을 호출시, labes = [ 리스트]형식으로 인자를 추가하면 각 카테고리명을 직접 지정해 줄 수 있다.
- 1. bins로 구분값이 5개이므로 –> 4개의 구간이 생긴다. 각 구간에 대한 카테고리명 4개를 담을 리스트를 만들자
group_names = ["Youth", "YoungAdult", "MiddleAged", "Senior"] - 2. pd.cut() 함수를 호출 할 때, 숫자데이터, 구간 구분값 + labes 인자를 추가로 주자.
pd.cut(ages, bins, labels= group_names) - 3. 각 구간별 성분의 갯수를 알고 싶으면 .value_counts()를 확인해야한다.
cats2 = pd.cut(ages, bins, labels= group_names)
각 구간 구분값으로 bins를 정의해서 나누었었는데, 이것이 귀찮다면, pandas에서 알아서 판단해서 데이터의 길이를 잘라주고 구간을 설정해주는 방법도 있다.
data = np.random.rand(20)
data라는 numpy의 1차원 array를 정의해놓은 다음, pandas의 pd.cut()함수를 호출하는데,
2번째 인자에서 각 구간 구분값(bins)이 리스트형식으로 넣어줬던 것을 –>
data 성분 값을 기준으로 자동으로 구간을 나누게 하기 위해서, 나눌 구간의 갯수만 입력해준다.(성분의 최소값 ~ 최대값를 보고 4개구간나눔)
추가로, 구간을 나눌 때 data 성분의 소수점 몇번째까지 고려해서 나눌지를 정하게 해주는 precision = 인자도 추가한다
- pd.cut(data, 4, precision = 2 ) 를 실행하게 되면,
20개의 data성분에 대해, 동일한 길이의 구간으로 4개를 나누었고, 기준은 소수2번째 자리까지를 기준으로 한다.
pd.cut(data, 4, precision = 2 )
한편, pandas에서는 qcut이라는 함수도 제공한다. 지정한 갯수만큼 구간을 정의하는데,
위에 cut()함수는 최대값 쵯소값만 고려해서 구간을 나눈 것에 비해
그 분포가 고려된, 분위 수를 구분값으로 구간을 나누는 함수다.
1000개의 랜덤 1차원 array를 데이터로 해보자. 이 때, 성분인덱싱으로 5개만 보자.
data2 = np.random.randn(1000)
data2[:5]
'빅데이터 관련 프로그래밍 > Python - bigdata(pandas 기초)' 카테고리의 다른 글
23. pandas 추가 – 데이터 분석 by 그룹화 +피벗 테이블(pivot_table) (0) | 2018.03.06 |
---|---|
22. pandas 추가 – 데이터 그룹화 함수 이해하기 (0) | 2018.03.05 |
20. pandas 추가 – 계층적 인덱싱(정렬함수, 통계함수적용, 인덱스와 칼럼 전환(stack,unstack)) (3) | 2018.03.02 |
19. pandas 추가 – 데이터 합치기 2가지 방식(merging, concatenating) (1) | 2018.03.01 |
18. Matplotlib - 기타 데이터 시각화 라이브러리(Seaborn, Bokeh, Folium) (0) | 2018.02.28 |