지난번 포스팅에서는 row나 column 기준으로 GroupBy의 Group을 지정할 수 있는 4가지 방법으로 Dicts, Series, Functions, Index Levels 를 소개하였습니다. 


이번 포스팅에서는 Python pandas에서 연속형 변수의 기술통계량 집계를 할 수 있는 GroupBy 집계 메소드와 함수 (GroupBy aggregation methods and functions)에 대해서 소개하겠습니다. 


(1) GroupBy 메소드를 이용한 집계 (GroupBy aggregation using methods): (ex) grouped.sum()

(2) 함수를 이용한 GroupBy 집계 (GroupBy aggregation using functions): grouped.agg(function)



[ Python pandas Group By 집계 메소드와 함수 ]



pandas에서 GroupBy 집계를 할 때 (1) pandas에 내장되어 있는 기술 통계량 메소드를 사용하는 방법과, (2) (사용자 정의) 함수를 grouped.agg(function) 형태로 사용하는 방법이 있습니다. GroupBy 메소드는 성능이 최적화되어 있어 성능면에서 함수를 사용하는 것보다 빠르므로, 메소드가 지원하는 집단별 기술통계량 분석 시에는 메소드를 이용하는게 좋겠습니다. 


NA 값은 모두 무시되고 non-NA 값들에 대해서만 GroupBy method가 적용됩니다. 


기술 통계량들이 어려운게 하나도 없으므로 이번 포스팅은 좀 쉬어가는 코너로 가볍게 소개합니다. 설명에 사용한 간단한 예제 데이터프레임과 'group'변수를 대상으로 GroupBy object를 만들어보겠습니다. 



# Importing common libraries

import numpy as np

import pandas as pd


# sample DataFrame

df = pd.DataFrame({'group': ['a', 'a', 'a', 'b', 'b', 'b'], 

                  'value_1': np.arange(6), 

                 'value_2': np.random.randn(6)})

df

groupvalue_1value_2
0a0-1.739302
1a10.851955
2a20.874874
3b3-0.461543
4b40.880763
5b5-0.346675




# Making GroupBy object

grouped = df.groupby('group')

grouped

<pandas.core.groupby.DataFrameGroupBy object at 0x11136f550>




  (1) GroupBy 메소드를 이용한 집계 (GroupBy aggregation using methods)


(1-1) count(), sum()

count(): 그룹 내 non-NA 개수 

sum(): 그룹 내 non-NA 합 

 

grouped.count()

value_1value_2
group
a33
b33


grouped.sum() # DataFrame

value_1value_2
group
a3-0.012473
b120.072545


*cf. grouped.size() 도 grouped.count()와 동일한 결과를 반환함



위의 예에서 보면 'value_1', 'value_2' 변수가 숫자형이므로 pandas가 알아서 잘 찾아서 count()와 sum()을 해주었으며, 반환된 결과는 데이터프레임입니다. 



만약 특정 변수에 대해서만 그룹별 요약/집계를 하고 싶다면 해당 변수를 indexing해주면 되며, 한개 변수에 대해서만 GroupBy 집계를 하면 반환되는 결과는 Series가 됩니다. 한개 변수에 대해 GroupBy 집계해서 나온 Series를 데이터프레임으로 만들고 싶으면 pd.DataFrame() 를 사용해서 집계 결과를 데이터프레임으로 변환해주면 됩니다. 



grouped.sum()['value_2'] # Series

group

a   -0.012473
b    0.072545 

Name: value_2, dtype: float64



pd.DataFrame(grouped.sum()['value_2']) # DataFrame 

value_2
group
a-0.012473
b0.072545





(1-2) 최소값, 최대값: min(), max()

min(): 그룹 내 non-NA 값 중 최소값 

max(): 그룹 내 non-NA 값 중 최대값 

 

grouped.min()

value_1value_2
group
a0-1.739302
b3-0.461543



grouped.max()

value_1value_2
group
a20.874874
b50.880763





(1-3) 중심 경향: mean(), median()

mean(): 그룹 내 non-NA 값들의 평균값 

median(): 그룹 내 non-NA 값들의 중앙값 

 

grouped.mean()

value_1value_2
group
a1-0.004158
b40.024182



grouped.median()

value_1value_2
group
a10.851955
b4-0.346675



※ 그룹별로 집계된 결과값의 변수에 접두사(Prefix)를 붙이려면 add_prefix() 를 사용합니다. 

예) df.groupby('key_col').mean().add_prefix('mean_')




(1-4) 퍼짐 정도: std(), var(), quantile()


표준편차, 분산 계산에 n-1 자유도를 사용했으므로 샘플표준편차, 샘플분산으로 봐야겠네요. 

quantile() 의 괄호 안에 0~1 사이의 값을 넣어주면 분위수를 계산해주며, 최소값과 최대값을 등분하여 그 사이를 interpolation 하여 분위수를 계산하는 방식입니다. 


std(): 그룹 내 표준편차

var(): 그룹 내 분산

quantile(): 그룹 내 분위수 


grouped.std() 

value_1value_2
group
a1.01.502723
b1.00.744042



grouped.var()

value_1value_2
group
a12.258176
b10.553598

 

 # interpolation

grouped.quantile(0.1) 

0.1value_1value_2
group
a0.2-1.221051
b3.2-0.438569




(1-5) first(), last()

first(): 그룹 내 non-NA 값 중 첫번째 값 

last(): 그룹 내 non-NA 값 중 마지막 값 

 

grouped.first()

value_1value_2
group
a0-1.739302
b3-0.461543


 

grouped.last()

value_1value_2
group
a20.874874
b5-0.346675





(1-6) describe()

describe(): 그룹 별 기술통계량 

- 옆으로 길게

 describe().T: 그룹 별 기술통계량 

- 세로로 길게

 

grouped.describe()['value_1']

countmeanstdmin25%50%75%max
group
a3.01.01.00.00.51.01.52.0
b3.04.01.03.03.54.04.55.0


 

grouped.describe()['value_1'].T

groupab
count3.03.0
mean1.04.0
std1.01.0
min0.03.0
25%0.53.5
50%1.04.0
75%1.54.5
max2.05.0





  (2) 함수를 이용한 GroupBy 집계: grouped.agg(function)


필요로 하는 집계함수가 pandas GroupBy methods에 없는 경우 사용자 정의 함수를 정의해서 집계에 사용할 수 있습니다. IQR(Inter-Quartile Range, Q3 - Q1) 를 사용자 정의 함수로 정의하고, 이를 grouped.aggregate() 혹은 grouped.agg() 의 괄호 안에 넣어서 그룹 별로 IQR를 계산해보겠습니다. 



def iqr_func(x):

    q3, q1 = np.percentile(x, [75, 25])

    iqr = q3 - q1

    return iqr




grouped.aggregate(function) 

grouped.agg(function) 

 

grouped.aggregate(iqr_func)

value_1value_2
group
a11.307088
b10.671153


 

grouped.agg(iqr_func)

value_1value_2
group
a11.307088
b10.671153



위에서 사용자 정의함수로 정의해서 그룹별로 집계한 결과가 맞게 나온건지 quantile() 메소드로 그룹별 Q3 와 Q1을 계산해서 확인해보니, 위의 grouped.agg(iqr_func)가 잘 계산한거 맞네요. 



grouped.quantile([0.75, 0.25])

value_1value_2
group
a0.751.50.863414
0.250.5-0.443674
b0.754.50.267044
0.253.5-0.404109

 


다음번 포스팅에서는 grouped.agg() 의 좀더 다양한 사용법을 소개하겠습니다. 


많은 도움이 되었기를 바랍니다. 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 파이썬의 자료 유형인 

 - 숫자(Number), 

 - 문자열(String), 

 - 리스트(List), 

 - 튜플(Tuple), 

 - 사전(Dictionary) 


중에서 먼저 숫자(Number)와 문자열(String)의 기본 사용법을 소개하였습니다. 


이번 포스팅에서는 지난 포스팅에 이어서 문자열에 특화되어 문자열 자료형을 다양하게 처리할 수 있는 함수인 문자열 메소드(String Methods) 에 대해서 알아보겠습니다. 


문자열 메소드를 숙지하고 있으면 동일한 목적의 문자열 전처리를 위해 직접 프로그래밍을 하는 것보다 문자열 메소드를 사용한 1~2줄의 코드면 해결되므로 업무 효율도 오르고, for loop 문을 쓰는 것보다 속도도 훨씬 빠릅니다. 


[참고]  메소드 (Method)


내장 함수(Built-in Function)와는 달리 문자열 자료형과 같이 특정 자료형이 가지고 있는 함수를 메소드(Method) 라고 합니다. 메소드는 객체 지향 프로그래밍의 기능에 대응하는 파이썬 용어입니다. 함수와 거의 동일한 의미이지만 메소드는 클래스의 멤버라는 점이 다릅니다. 



평소에 공부해놓고 '아, 문자열 메소드에 이런 기능이 있었지!' 정도는 기억해놓고 있어야 바로 찾아서 쓰기 쉽겠지요? 아래에 문자열 메소드들을 기능에 따라서 그룹핑을 해보았는데요, 저의 경우 len(), find(), lower(), upper(), lstrip(), rstrip(), split(), splitlines(), replace(), join(), zfill() 등을 종종 사용하는 편이네요. 



[ 파이썬 문자열 메소드 (String Methods in Python) ]



이번 포스팅은 https://www.tutorialspoint.com/python/python_strings.htm  사이트에 있는 영문 소개자료를 참고하여 작성하였습니다. 문자열 메소드의 기능 설명만 되어 있어서 좀더 이해하기 쉽도록 예제를 추가로 만들어보았습니다. 

expandtabs(), maketrans() 등 일부 메소드는 제가 써본적도 없고 앞으로 거의 쓸 일이 없을 것 같아서 주관적으로 판단해서 몇 개 빼고 소개하는 것도 있습니다. 


하나씩 예을 들어 살펴보겠습니다. 




1. 문자열 계산 관련 메소드 (String methods based on calculation)


len() : 문자열 길이



# len() : Returns the length of the string

>>> a = 'I Love Python'

>>> len(a)

13

 



 min(), max() : 문자열 내 문자, 혹은 숫자의 최소값, 최대값 (알파벳 순서, 숫자 순서 기반)



# max(str), min(str) : Returns the max, min alphabetical character from the string str

>>> d = 'abc'

>>> f = '123'

>>> 

>>> min(d)

'a'

>>> max(d)

'c'

>>> 

>>> min(f)

'1'

>>> max(f)

'3'

 



 count() :  문자열 안에서 매개변수로 입력한 문자열이 몇 개 들어있는지 개수를 셈 

                (begin, end 위치 설정 가능)



# count() : Counts how many times str occurs in string

>>> a = 'I Love Python'

>>> a.count('o')

2

>>> 

>>> a = 'I Love Python'

>>> a.count('o', 7, len(a)) # count(string, begin, end)

1

>>> 

>>> a.count('k') # there is no 'k' character in 'a' string

0

 





2. 문자열에 특정 문자 들어있는지 여부, 어디에 위치하고 있는지 찾아주는 메소드


  startswith() : 문자열이 매개변수로 입력한 문자열로 시작하면 True, 그렇지 않으면 False 반환



# startswith(): Determines if string or a substring of string

>>> a = 'I Love Python'

>>> a.startswith('I')

True

>>> a.startswith('I Lo')

True

>>> a.startswith('U')

False

 



  endswith() : 문자열이 매개변수로 입력한 문자열로 끝나면 True, 그렇지 않으면 False 반환



# endswith(): Determines if string or a substring of string

>>> a = 'I Love Python'

>>> a.endswith('Python')

True

>>> a.endswith('Pycham')

False

 



 find() : 문자열에 매개변수로 입력한 문자열이 있는지를 앞에서 부터 찾아 index 반환, 없으면 '-1' 반환



# find() : Search forwards, Determine if str occurs in string and return the index

>>> a = 'I Love Python'

>>> a.find('o')

3

>>> a.find('k') # if there is no string, then '-1'

-1

 



  rfind() : 문자열에 매개변수로 입력한 문자열이 있는지를 뒤에서 부터 찾아 index 반환, 없으면 '-1' 반환



# rfind() : Same as find(), but search backwards in string

>>> a = 'I Love Python'

>>> a.rfind('o')

11

 



 index() :  find()와 기능 동일하나, 매개변수로 입력한 문자열이 없으면 ValueError 발생



# index(): Same as find(), but raises an exception if str not found

>>> a = 'I Love Python'

>>> a.index('o')

3

>>> a.index('k') # ValueError: substring not found

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

ValueError: substring not found

 



 rindex() : index()와 기능 동일하나, 뒤에서 부터 매개변수의 문자열이 있는지를 찾음



# rindex(): Same as index(), but search backwards in string

>>> a = 'I Love Python'

>>> a.rindex('o')

11

>>> a.rindex('k') # ValueError: substring not found

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

ValueError: substring not found

 





3. 숫자, 문자 포함 여부 확인하는 메소드


  isalnum() : 문자열이 알파벳과 숫자로만 이루어졌으면 True, 그렇지 않으면 False



>>> a = 'I Love Python'

>>> d = 'abc'

>>> e = '123abc'

>>> f = '123'

>>> 

# isalnum() : Returns true if string has at least 1 character and 

#                  all characters are alphanumeric and false otherwise

>>> a.isalnum() # False

False

>>> d.isalnum() # True

True

>>> e.isalnum() # True

True

>>> f.isalnum() # True

True




  isalpha() : 문자열이 알파벳(영어, 한글 등)으로만 이루어졌으면 True, 그렇지 않으면 False



>>> a = 'I Love Python'

>>> d = 'abc'

>>> e = '123abc'

>>> f = '123'

>>> 

# isalpha() : Returns true if string has at least 1 character 

#                 and all characters are alphabetic and false otherwise

>>> a.isalpha() # False (there is space between characters)

False

>>> d.isalpha() # True

True

>>> e.isalpha() # False

False

>>> f.isalpha() # False

False




  isdigit() : 문자열이 숫자만 포함하고 있으면 True, 그렇지 않으면 False, isnumeric()과 동일



>>> a = 'I Love Python'

>>> d = 'abc'

>>> e = '123abc'

>>> f = '123'

>>> 

# isdigit() : Returns true if string contains only digits and false otherwise

>>> a.isdigit() # False 

False

>>> d.isdigit() # False

False

>>> e.isdigit() # False

False

>>> f.isdigit() # True

True




  isnumeric() : 문자열이 숫자로만 이루어져 있으면 True, 그렇지 않으면 False, isdigit()과 동일



>>> a = 'I Love Python'

>>> d = 'abc'

>>> e = '123abc'

>>> f = '123'

>>> 

# isnumeric(): Returns true if a unicode string contains only numeric characters

>>> a.isnumeric() # False

False

>>> d.isnumeric() # False

False

>>> e.isnumeric() # False

False

>>> f.isnumeric() # True

True

 



  isdecimal() : 문자열이 10진수 문자이면 True, 그렇지 않으면 False



>> a = 'I Love Python'

>>> d = 'abc'

>>> e = '123abc'

>>> f = '123'

>>> 

# isdecimal(): Returns true if a unicode string contains only decimal characters

>>> a.isdecimal() # False

False

>>> d.isdecimal() # False

False

>>> e.isdecimal() # False

False

>>> f.isdecimal() # True 

True

 





4. 대문자, 소문자 여부 확인하고 변환해주는 문자열 메소드


  islower() : 문자열이 모두 소문자로만 되어있으면 True, 그렇지 않으면 False



>>> a = 'I Love Python'

>>> g = 'i love python'

>>> h = 'I LOVE PYTHON'

>>> 

# islower(): Returns true if string has at least 1 cased character 

#            and all cased characters are in lowercase and false otherwise

>>> a.islower() # False

False

>>> g.islower() # True

True

>>> h.islower() # False

False

 



  isupper() : 문자열이 모두 대문자로만 되어있으면 True, 그렇지 않으면 False



>>> a = 'I Love Python'

>>> g = 'i love python'

>>> h = 'I LOVE PYTHON'

>>> 

# isupper(): Returns true if string has at least one cased character 

#           and all cased characters are in uppercase and false otherwise

>>> a.isupper() # False

False

>>> g.isupper() # False

False

>>> h.isupper() # True

True

 



  lower() : 문자열 내 모든 대문자를 모두 소문자(a lowercase letter)로 변환



>>> a = 'I Love Python'

>>> 

# lower(): Converts all uppercase letters in string to lowercase

>>> a.lower()

'i love python'




 upper() :  문자열 내 모든 소문자를 모두 대문자(a uppercase letter)로 변환



>>> a = 'I Love Python'

>>> 

# upper(): Converts lowercase letters in string to uppercase

>>> a.upper()

'I LOVE PYTHON'

 



 swapcase() : 문자열 내 소문자는 대문자로 변환, 대문자는 소문자로 변환



# swapcase(): Inverts case for all letters in string

>>> a = 'I Love Python'

>>> a.swapcase() # 'i lOVE pYTHON'

'i lOVE pYTHON'

>>> 

>>> g = 'i love python'

>>> g.swapcase() # 'I LOVE PYTHON' (same as upper())

'I LOVE PYTHON'

>>> 

>>> h = 'I LOVE PYTHON'

>>> h.swapcase() # 'i love python' (same as lower())

'i love python'

 



  istitle() : 문자열이 제목 형식에 맞게 대문자로 시작하고 이후는 소문자이면 True, 그렇지 않으면 False



>>> a = 'I Love Python'

>>> g = 'i love python'

>>> h = 'I LOVE PYTHON'

>>> 

# istitle(): Returns true if string is properly "titlecased" and false otherwise

>>> a.istitle() # True

True

>>> g.istitle() # False

False

>>> h.istitle() # False

False

 



 title() : 문자열을 제목 형식(titlecased)에 맞게 시작은 대문자로, 나머지는 소문자로 변환 



>>> g = 'i love python'

>>> h = 'I LOVE PYTHON'

>>> 

# title(): Returns "titlecased" version of string, that is, 

#          all words begin with uppercase and the rest are lowercase

>>> g.title() # 'I Love Python'

'I Love Python'

>>> h.title() # 'I Love Python'

'I Love Python'

 



  capitalize)=() : 문자열 내 첫번째 문자를 대문자로 변환하고, 나머지는 모두 소문자로 변환



>>> a = 'I Love Python'

>>> g = 'i love python'

>>> h = 'I LOVE PYTHON'

>>> 

# capitalize(): Capitalizes first letter of string

>>> a.capitalize() # 'I love python'

'I love python'

>>> g.capitalize() # 'I love python'

'I love python'

>>> h.capitalize() # 'I love python'

'I love python'

 





5. 공백 존재 여부 확인 및 처리하기 문자열 메소드 


 lstrip() : 문자열의 왼쪽에 있는 공백을 제거



# lstrip() : Removes all leading whitespace in string

>>> b = '      I Love Python'

>>> b.lstrip()

'I Love Python'




  rstrip() : 문자열의 오른쪽에 있는 공백을 제거



# rstrip() : Removes all trailing whitespace of string

>>> c = 'I Love Python      '

>>> c.rstrip()

'I Love Python'

 



  strip() : 문자열의 양쪽에 있는 공백을 제거



# strip() : Performs both lstrip() and rstrip() on string

>>> '     I Love Python     '.strip()

'I Love Python'

 



  isspace() : 문자열이 단지 공백(whitespace)으로만 되어있을 경우 True, 그렇지 않으면 False



# isspace(): Returns true if string contains only whitespace characters and false otherwise

>>> i = '     '

>>> j = '      I Love Python'

>>> 

>>> i.isspace() # True

True

>>> j.isspace() # False

False

 



  center(width) : 총 길이가 매개변수로 받는 문자열폭(width)만큼 되도록 공백을 추가하여 중앙 정렬



# center(): Returns a space-padded string with the original string 

#                centered to a total of width columns

>>> a = 'I Love Python'

>>> a.center(21)

'    I Love Python    '

 





6. 문자열을 나누고, 붙이고, 교체하고, 채우는 문자열 메소드 (split, join, replace, fill)


  split() : 문자열을 구분자(delimiter, separator) 기준에 따라 나누기


split()은 상당히 자주 사용하는 문자열 메소드 입니다. 



# split(): Splits string according to delimiter str (space if not provided) 

#    and returns list of substrings; split into at most num substrings if given

>>> x = 'haha, hoho, hihi'

>>> x.split(sep=',') # as a list ['haha', ' hoho', ' hihi']

['haha', ' hoho', ' hihi']




>>> ha, ho, hi = x.split(sep=',')

>>> ha

'haha'

>>> ho

' hoho'

>>> hi

' hihi'

 



>>> a = 'I Love Python'

>>> a.split(' ') # without arg 'sep='

['I', 'Love', 'Python']

>>> a.split() # default delimiter is space if not provided

['I', 'Love', 'Python'] 

 



  splitlines() : 여러개의 줄로 이루어진 문자열을 줄 별로 구분하여 리스트 생성



# splitlines(): returns a list with all the lines in string, 

#     optionally including the line breaks (if num is supplied and is true)

>>> y = 'haha, \nhoho, \nhihi'

>>> y

'haha, \nhoho, \nhihi'

>>> y.splitlines() # ['haha, ', 'hoho, ', 'hihi']

['haha, ', 'hoho, ', 'hihi']

 



 replace(old, new, max) : old 문자열을 new 문자열로 교체.  단, max 매개변수 있으면, max 개수 만큼만 교체하고 이후는 무시



# replace(old, new): Replaces all occurrences of old in string with new 

#        or at most max occurrences if max given

>>> a = 'I Love Python'

>>> a.replace('Python', 'R')

'I Love R'

>>> 

>>> 

>>> a_2 = 'I Love Python, Python, Python, Python, Python~!!!'

>>> a_2.replace('Python', 'R', 3) # str.replace(old, new, max)

'I Love R, R, R, Python, Python~!!!'




 join() :  여러개의 문자열을 구분자(separator) 문자열을 사이에 추가하여 붙이기


join()은 꽤 자주 쓰는 문자열 메소드 중의 하나입니다. 



# join(): Merges (concatenates) the string representations of elements 

#         in sequence seq into a string, with separator string

>>> mylist = ['I', 'Love', 'Python']

>>> print(mylist)

['I', 'Love', 'Python']

>>> 

>>> mystring = '_'.join(mylist)

>>> print(mystring) # 'I_Love_Python'

I_Love_Python

 



# To concatenate item in list to strings with join() method

>>> mylist_num = [1, 2, 3, 4, 5]

>>> print(mylist_num) # [1, 2, 3, 4, 5]

[1, 2, 3, 4, 5]

>>> 

>>> mylist_str = ''.join(map(str, mylist_num))

>>> print(mylist_str) # 12345

12345

>>> 

>>> '_'.join(map(str, mylist_num)) # '1_2_3_4_5'

'1_2_3_4_5'

 



 zfill(width) : 문자열을 매개변수 width만큼 길이로 만들되, 추가로 필요한 자리수만큼 '0'을 채움



# zfill(width): Returns original string leftpadded with zeros to a total of width characters; 

#      intended for numbers, zfill() retains any sign given (less one zero)

>>> f = '123'

>>> f.zfill(10)

'0000000123' 




 ljust(width[, fillchar]) : 문자열을 매개변수 width만큼 길이로 만들되, 왼쪽은 원본 문자열로 채우고, 

오른쪽에 추가로 필요한 자리수만큼 매개변수 fillchar 문자열로 채움



# ljust(): Returns a space-padded string with the original string left-justified 

#          to a total of width columns

# str.ljust(width[, fillchar])

>>> a = 'I Love Python'

>>> a.ljust(20, 'R')

'I Love PythonRRRRRRR'

 



 rjust(width[, fillchar]) : 문자열을 매개변수 width만큼 길이로 만들되, 오른쪽은 원본 문자열로 채우고, 

 왼쪽에 추가로 필요한 자리수만큼 매개변수 fillchar 문자열로 채움



# rjust(): Returns a space-padded string with the original string right-justified 

#          to a total of width columns

>>> a.rjust(20, 'R')

'RRRRRRRI Love Python'

>>> a.rjust(20, ' ')

'       I Love Python'

 



다음번 포스팅에서는 문자열의 포맷 메소드(string formatting opertor)에 대해서 알아보겠습니다. 


많은 도움 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾸욱 눌러주세요. ^^



728x90
반응형
Posted by Rfriend
,

그동안 R dplyr package 의 기본 함수와 chaining operator (%>%) 에 대해서 알아보았습니다.

이번 포스팅에서는 R dplyr package를 사용해서 그룹별로 행의 개수 세기 (counting rows up by group using dplyr package)를 해보겠습니다.  counting 하는 것은 기본 중의 기본이라서 탐색적분석(Exploratory Data Analysis) 할 때 수시로 사용하므로 알아두면 유용하겠지요?!

혹시 chaining operator (%>%, shift+ctr+M) 에 대해서 모르는 분은 http://rfriend.tistory.com/236 포스팅을 참고하세요.  

 R dplyr package 의 summarise(n = n()), summarise(dist_n = distinct_n(factor)), tally(), count() 함수에 대해서 하나씩 예를 들면서 설명하겠습니다.  summarise(n = n()), summarise(dist_n = distinct_n(factor)), tally() 함수는 group_by()를 chaining 해서 사용하며, 마지막의 count() 함수만 group_by() chaining 없이 사용합니다.

 

[ 그룹별로 행의 개수 세기 (counting rows up by group using dplyr package) ]

 

예시에 사용할 데이터는 MASS package에 내장되어 있는 Cars93 데이터 프레임입니다.

 ##--------------------------------------------------
## counting things up by group, using dplyr package
##--------------------------------------------------

library(dplyr)
library(MASS)

str(Cars93)

 

차종(Type)별로 행의 개수 (차량의 개수)를 세어보겠습니다.

 
'data.frame':	93 obs. of  28 variables:
 $ Manufacturer      : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model             : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type              : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price         : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price             : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price         : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city          : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway       : int  31 25 26 26 30 31 28 25 27 25 ...
 $ AirBags           : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
 $ DriveTrain        : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
 $ Cylinders         : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
 $ EngineSize        : num  1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
 $ Horsepower        : int  140 200 172 172 208 110 170 180 170 200 ...
 $ RPM               : int  6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
 $ Rev.per.mile      : int  2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
 $ Man.trans.avail   : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
 $ Fuel.tank.capacity: num  13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
 $ Passengers        : int  5 5 5 6 4 6 6 6 5 6 ...
 $ Length            : int  177 195 180 193 186 189 200 216 198 206 ...
 $ Wheelbase         : int  102 115 102 106 109 105 111 116 108 114 ...
 $ Width             : int  68 71 67 70 69 69 74 78 73 73 ...
 $ Turn.circle       : int  37 38 37 37 39 41 42 45 41 43 ...
 $ Rear.seat.room    : num  26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
 $ Luggage.room      : int  11 15 14 17 13 16 17 21 14 18 ...
 $ Weight            : int  2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
 $ Origin            : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
 $ Make              : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...
 $ sub_yn            : num  0 0 0 0 0 0 0 0 0 0 ...

 

 

자, 이제 dplyr 패키지를 사용해서 실습을 해보시지요.

 

(1)  dataframe %>% group_by(factor) %>% summarise(n = n())

먼저 summarise(n = n()) 함수입니다. group_by() 와 함께 chaining 해서 사용하는 예시입니다.

 

# using summarise(n = n()) in {dplyr} package
Cars93 %>%
  group_by(Type) %>%
  summarise(n = n())

 

 

# A tibble: 6 x 2
     Type     n
   <fctr> <int>
1 Compact    16
2   Large    11
3 Midsize    22
4   Small    21
5  Sporty    14
6     Van     9

 

 

 

 (2) dataframe %>% group_by(factor) %>% summarise(n = n(), n_dist = n_distinct())

차종(Type)별로 자동차 대수(행의 개수)와 더불어서, 차종별로 유일한 제조회사(Manufacturer)의 개수를 세어서 n_distinct_maker 라는 새로운 변수를 추가해보겠습니다.

 

# adding the number of distinct manufacturers
# by using multiple summaries inside summarise()
# like summarize(n = n(), n_distinct = n_distinct())
Cars93 %>%
  group_by(Type) %>%
  summarize(n = n(),
            n_distinct_maker = n_distinct(Manufacturer)) 

 

 

# A tibble: 6 x 3
     Type     n       n_distinct_maker
   <fctr> <int>            <int>
1 Compact    16               15
2   Large    11               10
3 Midsize    22               20
4   Small    21               16
5  Sporty    14               12
6     Van     9                8

 

 

 

 (3) dataframe %>% group_by(factor) %>% tally()

 R dplyr에는 summarise(n = n()) 함수와 동일한 기능, 동일한 결과를 반환하는 또 다른 함수로 tally() 가 있습니다.  group_by() 와 함께 chaining 해서 사용합니다.

 

# using tally() in {dplyr} package
Cars93 %>%
  group_by(Type) %>%
  tally()

 

 

# A tibble: 6 x 2
     Type     n
   <fctr> <int>
1 Compact    16
2   Large    11
3 Midsize    22
4   Small    21
5  Sporty    14
6     Van     9

 

 

 (4) dataframe %>% count(factor)

마지막으로 소개할 count(factor) 함수는 group_by(factor) chaining 없이 사용합니다.  위의 3개 보다 좀더 간단하긴 한데요, 해석이나 가독성 면에서 group_by() 가 들어가게 프로그램 짜는 것을 더 선호하는 사용자도 있을 듯 합니다. 

 

# using count() in {dplyr} package}
Cars93 %>%
  count(Type) # doing both grouping and counting (no need for group_by())

 

 

# A tibble: 6 x 2
     Type     n
   <fctr> <int>
1 Compact    16
2   Large    11
3 Midsize    22
4   Small    21
5  Sporty    14
6     Van     9

 

 


 

참고로, dplyr 패키지 말고도 {base} package의 table() 함수나, {sqldf} package의 sqldf() 함수를 사용해도 그룹별 관측치 개수 세기가 가능합니다.  아래 참고하세요.

 

(대안 1) {base} package의 table() 함수

counting 결과 제시 포맷이 위의 dplyr 패키지를 사용했을 때와는 다릅니다.  dplyr 패키지를 사용한 그룹별 행의 개수 세기에서는 차종(Type)이 별도 행, count 개수 n이 별도 행으로 제시가 되었었는데요, base 패키지의 table() 함수는 아래의 예시처럼 옆으로 차종이 죽~ 늘어서 있습니다.

 

## alternative : table()

# using table() in {base} package
table(Cars93$Type)

 

 

Compact   Large Midsize   Small  Sporty     Van 
     16      11      22      21      14       9 

 

 

table() 분석 결과는 데이터 프레임이 아니라 'table'입니다.

> str(table(Cars93$Type))
 'table' int [1:6(1d)] 16 11 22 21 14 9
 - attr(*, "dimnames")=List of 1
  ..$ : chr [1:6] "Compact" "Large" "Midsize" "Small" ...

 

 

(대안 2) {sqldf} 패키지의 sqldf() 함수

 

## alternative : sqldf() 

# using {sqldf} package
install.packages("sqldf")
library(sqldf)
sqldf('
      select Type, count(*) as n
      from Cars93
      group by Type
      order by Type
      ')

 

 
     Type  n
1 Compact 16
2   Large 11
3 Midsize 22
4   Small 21
5  Sporty 14
6     Van  9

 

 

sqldf 패키지의 sqldf() 함수에 대한 좀더 자세한 사용법은 http://rfriend.tistory.com/79  포스팅을 참고하세요.

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되셨다면 아래의 '공감 ~ ♡'를 눌러주세요. ^^

728x90
반응형
Posted by Rfriend
,