이전 포스팅에서 사전 자료형(dictionary data type)을 만드는 방법과 기본 사용법(https://rfriend.tistory.com/333), 사전 자료형 내장함수와 메소드(https://rfriend.tistory.com/334)에 대해서 설명한 적이 있습니다. 


사전 자료형을 중괄호로 묶여서 {키(key) : 값(value)} 의 쌍으로 이루어진다고 했으며, hash table type 으로 키를 해싱해놓고 있다가, 키를 기준으로 값을 찾으려고 하면 매우 빠른 속도로 찾아주는 효율적이고 편리한 자료형이라고 소개를 했었습니다. 


이번 포스팅에서는 사전 자료형을 


(1) 키를 기준으로 오름차순 정렬 (sort by key in ascending order)

(2) 키를 기준으로 내림차순 정렬 (sort by key in descending order)

(3) 값을 기준으로 오름차순 정렬 (sort by value in ascending order)

(4) 값을 기준으로 내림차순 정렬 (sort by value in descending order) 


하는 방법을 알아보겠습니다. 




{키 : 값} 쌍으로 이루어진 간단한 예제 사전 자료형을 만들어보겠습니다. (프로그래밍 언어별 인기도)


# make a dictionary

pgm_lang = {

    "java": 20, 

    "javascript": 8, 

    "c": 7,  

    "r": 4, 

    "python": 28 } 




 (1) 를 기준으로 오름차순 정렬 (sort by key in ascending order): sorted()


pgm_lang 사전형 자료를 키(key)를 기준으로 알파벳 오름차순으로 정렬해보겠습니다. dict.keys()를 정렬하면 keys 만 정렬된 값을 반환하며, dict.items()를 정렬하면 키(key)를 기준으로 정렬하되 키와 값을 튜플로 묶어서 정렬된 값을 반환합니다. 



sorted(pgm_lang.keys())


['c', 'java', 'javascript', 'python', 'r']



sorted(pgm_lang.items())


[('c', 7), ('java', 20), ('javascript', 8), ('python', 28), ('r', 4)]




그러면, 키를 기준으로 정렬한 (키, 값) 쌍의 튜플을 for loop 을 써서 한줄씩 인쇄를 해보겠습니다. 

첫번째 예는 일반적인 for loop이며, 두번째는 list comprehension 을 이용하여 같은 결과로 인쇄한 예입니다. 



for key, value in sorted(pgm_lang.items()):

    print(key, ":", value)


c : 7
java : 20
javascript : 8
python : 28
r : 4


# list comprehension

[print(key, ":", value) for (key, value) in sorted(pgm_lang.items())]


c : 7
java : 20
javascript : 8
python : 28
r : 4

[None, None, None, None, None]




키를 기준으로 정렬할 때, lambda 함수를 사용하여 새롭게 정의한 키(custom key function, logic)를 사용할 수도 있습니다. 아래 예는 키의 길이(length)를 기준으로 오름차순 정렬을 해본 것입니다. 



# sort a dictionary by custom key function (eg. by the length of key strings)

pgm_lang_len = sorted(pgm_lang.items(), key = lambda item: len(item[0])) # key: [0]


for key, value in pgm_lang_len:

    print(key, ":", value)


c : 7
r : 4
java : 20
python : 28
javascript : 8





 (2) 를 기준으로 내림차순 정렬 (sort by key in descending order): reverse=True


내림차순으로 정렬하려면 reverse=True 옵션을 추가해주면 됩니다. 



# sorting in reverse order

sorted(pgm_lang.keys(), reverse=True)


['r', 'python', 'javascript', 'java', 'c']



for (key, value) in sorted(pgm_lang.items(), reverse=True):

    print(key, ":", value)


r : 4
python : 28
javascript : 8
java : 20
c : 7





 (3) 을 기준으로 오름차순 정렬 (sort by value in ascending order)


값(value)을 기준으로 정렬하려면 앞서 소개했던 lambda 함수를 이용하여 키(key)로 사용할 기준이 값(value), 즉 item[1] 이라고 지정을 해주면 됩니다. (키는 item[0], 값은 item[1] 로 indexing)



sorted(pgm_lang.items(), key = lambda item: item[1]) # value: [1]


[('r', 4), ('c', 7), ('javascript', 8), ('java', 20), ('python', 28)]

 



값을 기준으로 오름차순 정렬한 결과를 for loop을 같이 사용하여 (키 : 값) 한쌍씩 차례대로 프린트를 해보겠습니다. (list comprehension 을 사용해도 결과 동일)



pgm_lang_val = sorted(pgm_lang.items(), key = lambda item: item[1])


for key, value in pgm_lang_val:

    print(key, ":", value)


r : 4
c : 7
javascript : 8
java : 20
python : 28


# list comprehension

[print(key, ":", value) for (key, value) in sorted(pgm_lang.items(), key = lambda item: item[1])]


r : 4
c : 7
javascript : 8
java : 20
python : 28
[None, None, None, None, None]





 (4) 을 기준으로 내림차순 정렬 (sort by value in descending order) 


위의 (3)번과 같이 값을 기준으로 정렬하므로 key=lambda x:x[1] 로 값(value, 즉 x[1])이 정렬 기준이라고 지정을 해주구요, 대신 내림차순이므로 reverse=True 옵션을 추가해주면 됩니다. 



# in reverse order

pgm_lang_val_reverse = sorted(pgm_lang.items()

                              reverse=True

                              key=lambda item: item[1])


for key, value in pgm_lang_val_reverse:

    print(key, ":", value)


python : 28
java : 20
javascript : 8
c : 7
r : 4




바로 위의 예에서는 for loop 순환할 때 key, value 2개 객체로 따로 따로 받았는데요, 바로 아래의 예처럼 items 로 (키, 값) 튜플로 받아서 items[0] 으로 키, items[1] 로 값을 indexing 할 수도 있습니다. 



# the same result with above

for items in pgm_lang_val_reverse:

    print(items[0], ":", items[1])


python : 28
java : 20
javascript : 8
c : 7
r : 4




이상으로 사전 자료형(dictionary)의 키, 값 기준 정렬(sorted)하는 4가지 방법 소개를 마치겠습니다. 



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


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 구분자(delimiter, separator)를 포함한 문자열 칼럼을 구분자를 기준으로 여러개의 칼럼으로 나누어서 DataFrame을 만드는 방법을 소개하겠습니다. 


그리고 PoestgreSQL, Greenplum DB에서도 구분자를 포함한 칼럼을 구분자를 기준으로 여러개의 칼럼으로 나누는 방법도 이어서 소개하겠습니다. 






 (1) pandas DataFrame 내 문자열 칼럼을 구분자로 분리하여 여러개의 칼럼 만들기


먼저 간단한 예를 들기 위해 ':' 구분자(delimiter, separator)를 가진 'col' 이라는 이름의 칼럼을 가진 pandas DataFrame을 만들어보겠습니다. 


import pandas as pd


df = pd.DataFrame({'col': ['a:1:20.3:S', 'b:2:10.5:C', 'c:3:51.9:A']})


df

col
0a:1:20.3:S
1b:2:10.5:C
2c:3:51.9:A




이제 원래의 'df' 라는 이름의 DataFrame 에 'col'변수를 그대로 둔 채로, ':' 구분자를 기준으로 'col' 문자열 칼럼을 분리(split) 하여 'group', 'id', 'value', 'grade' 라는 새로운 4개의 칼럼을 생성하여 추가해보겠습니다. split() 문자열 메소드는 split(separator, maxsplit) 의 형식으로 사용합니다. 



df[['group', 'id', 'value', 'grade']] = pd.DataFrame(df.col.str.split(':', 3).tolist())


df

colgroupidvaluegrade
0a:1:20.3:Sa120.3S
1b:2:10.5:Cb210.5C
2c:3:51.9:Ac351.9A

 



원래의 'col' 이름의 칼럼이 필요 없을 경우 원래의 DataFrame을 덮어쓰거나, 아니면 'col'을 포함하지 않는 새로운 DataFrame을 만들어주면 됩니다. 



df2 = pd.DataFrame(df.col.str.split(':', 3).tolist()

                   columns = ['group', 'id', 'value', 'grade'])


df2

groupidvaluegrade
0a120.3S
1b210.5C
2c351.9A

 



문자열(string)을 분리(split)해서 만든 새로운 칼럼들은 전부 문자열(string) 데이터 형식입니다. 이중에서 'id'와 'value' 칼럼을 숫자형(numeric)으로 변경하는 방법은 https://rfriend.tistory.com/470 포스팅을 참고하세요. 



df2.dtypes

group    object
id       object
value    object
grade    object
dtype: object

 





 (2) PostgreSQL, GPDB에서 문자열 칼럼을 구분자로 분리하여 여러개 칼럼 만들기


PostgreSQL, Greenplum DB에서는 split_part(string_column, separator, field_number) 의 형식으로 문자열 칼럼을 나눌 수 있습니다. 




위의 Python pandas DataFrame에서 사용했던 것과 동일한 예제 Table을 만들어서, 'col' 문자열 칼럼을 'group', 'id', value', 'grade'의 4개의 문자열(string)을 가진 새로운 Table을 만들어보겠습니다. 


-- make a table

DROP TABLE IF EXISTS grp_val_grade;

CREATE TABLE grp_val_grade (

col varchar(100) NOT NULL

);


INSERT INTO grp_val_grade VALUES ('a:1:20.3:S');

INSERT INTO grp_val_grade VALUES('b:2:10.5:C');

INSERT INTO grp_val_grade VALUES('c:3:51.9:A');


SELECT * FROM grp_val_grade; 





다음으로 split_part(string_column, separator, field_number) 함수를 사용해서 문자열 칼럼을 ':' 구분자를 기준으로 나누어서 새로운 칼럼을 만들어보겠습니다. 



-- split a column by delimeter and make 4 columns

DROP TABLE IF EXISTS grp_val_grade2;

CREATE TABLE grp_val_grade2 AS (

SELECT 

col

, split_part(col, ':', 1) AS group

, split_part(col, ':', 2) AS id 

, split_part(col, ':', 3) AS value

, split_part(col, ':', 4) AS grade

FROM grp_val_grade

);


SELECT * FROM grp_val_grade2;




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


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

R 데이터 구조(structure), 유형(class) 에 대해서 예전에 포스팅을 했던 적이 있습니다.

 

R 데이터 구조(structure)에는 스칼라(scala), 벡터(vector), 요인(factor), 행렬(matrix), 데이터프레임(dataframe), 리스트(list) 가 있다고 소개를 해드렸습니다. (☞ R 데이터 구조 포스팅 보러가기)

 

그리고 R 데이터 유형(class)에는 숫자형 (numeric), 문자형 (character), 요인형 (factor) 등이 있다고 말씀드렸습니다. 오늘 포스팅에서는 R 데이터 객체의 유형을 (1) 확인, (2) 전환 하는 함수에 대해서 알아보도록 하겠습니다.

 

숫자형, 문자형에 대해서는 쉽게 알 수 있으므로, 요인형(factor)이 뭐고 왜 필요한 지에, 어디에 써 먹는지에 대해서 먼저 간략히 소개를 드린 후에 객체 유형 확인, 전환 함수로 넘어가도록 하겠습니다.  왜 공부해야 하는지도 모르면서 진도를 빼면 다 배우고 나서 'so what?' 하겠지요?  (물론, 나중에 통계분석, 그래프 쪽 가면 그때 가서 '아, 이래서 이거 배웠었구나...'하고 한번 더 복습하시면 되겠습니다.)

 

 

요인(factor)은 무엇이고, 어디에 써먹나? 

 

일단, 요인(factor)은 "~별 숫자형 벡터의 평균, ~별 숫자형 벡터의 합계" 등과 같이 요약 통계량을 집단이나 특성에 따라서 분석할 때 사용합니다. OLAP으로 치자면 '차원(dimension)' 역할을 하는 것이 되겠습니다.

 

MASS 패키지에 내장된 Cars93 데이터 프레임 내 변수들을 가지고 예를 들어보겠습니다.  

자동차 Type(Levels: Compact, Large, MidsizeSmall, Sporty, Van) 요인별로 고속도로 연비(MPG.highway)의 평균(mean)을 계산해보도록 하겠습니다.

 

> library(MASS) > str(Cars93) # 데이터 구조 데이터 프레임, 관측치 93개, 변수 27개 'data.frame': 93 obs. of 27 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 ...




 
> head(Cars93) # 상위 6개 관측치
  Manufacturer   Model    Type Min.Price Price Max.Price MPG.city MPG.highway            AirBags DriveTrain
1        Acura Integra   Small      12.9  15.9      18.8       25          31               None      Front
2        Acura  Legend Midsize      29.2  33.9      38.7       18          25 Driver & Passenger      Front
3         Audi      90 Compact      25.9  29.1      32.3       20          26        Driver only      Front
4         Audi     100 Midsize      30.8  37.7      44.6       19          26 Driver & Passenger      Front
5          BMW    535i Midsize      23.7  30.0      36.2       22          30        Driver only       Rear
6        Buick Century Midsize      14.2  15.7      17.3       22          31        Driver only      Front
  Cylinders EngineSize Horsepower  RPM Rev.per.mile Man.trans.avail Fuel.tank.capacity Passengers Length Wheelbase
1         4        1.8        140 6300         2890             Yes               13.2          5    177       102
2         6        3.2        200 5500         2335             Yes               18.0          5    195       115
3         6        2.8        172 5500         2280             Yes               16.9          5    180       102
4         6        2.8        172 5500         2535             Yes               21.1          6    193       106
5         4        3.5        208 5700         2545             Yes               21.1          4    186       109
6         4        2.2        110 5200         2565              No               16.4          6    189       105
  Width Turn.circle Rear.seat.room Luggage.room Weight  Origin          Make
1    68          37           26.5           11   2705 non-USA Acura Integra
2    71          38           30.0           15   3560 non-USA  Acura Legend
3    67          37           28.0           14   3375 non-USA       Audi 90
4    70          37           31.0           17   3405 non-USA      Audi 100
5    69          39           27.0           13   3640 non-USA      BMW 535i
6    69          41           28.0           16   2880     USA Buick Century
>

> > # 함수 명령문 형식: with(dataset, tapply(numeric vector, factor, function)) > with(Cars93, tapply(MPG.highway, Type, mean)) Compact Large Midsize Small Sporty Van 29.87500 26.72727 26.72727 35.47619 28.78571 21.88889

 

 

 

그래프를 그릴 때도 요인(factor)별로 구분해서 그린 후에 비교를 해보면 인사이트를 뽑아내는데 유용하게 사용할 수 있습니다. ggplot2 패키지를 이용해서 자동차 Type(Levels: Compact, Large, MidsizeSmall, Sporty, Van) 요인별로 고속도로 연비(MPG.highway)의 히스토그램을 그려보도록 하겠습니다.

 

> install.packages("ggplot2") # ggplot2 패키지 설치

> library(ggplot2) # ggplot2 패키지 호출

> # 명령식 형식: qplot(numeric vector, data=dataset, facets=factor~. , binwidth=n) > qplot(MPG.highway, data=Cars93, facets = Type~. , binwidth=2)

 

 

 

 

 그래프의 오른쪽에 보면 6개의 자동차 Type별로 구분이 되어서 히스토그램이 그려졌음을 알 수 있습니다.

 

위의 2개의 예에서 보는 것처럼 요인(factor)이 통계분석, 그래프 분석에 유용하게 쓰이므로, 반드시 요인(factor)으로 데이터 객체를 관리해야 하는것이 생깁니다. '요인'이 들어와 있어야 할 자리에 '숫자형'이나 '문자형'이 들어와 있으면 '요인형'을 넣어서 써야 하는 함수 명령문이 안 먹겠지요.  반면에, 문자(character)나 숫자(numeric)로 인식을 해야 하는게 있는데 R에 요인(factor)로 잘못 인식이 된 경우도 있을 수 있으며, 이런 경우 에러가 나는 주요 범인이 되곤 합니다. 

 

그래서 데이터셋을 받으면 제일 처음 하는 일이 str(), head()로 데이터셋의 구조, 변수, 관측치 등을 살피고, 그 다음으로 class()로 데이터 객체별 속성을 확인해보는 것이 반드시 필요합니다. 그리고 분석가가 원하는 속성으로 데이터가 안들어가 있다면 속성을 분석 목적에 맞게 전환해줄 필요가 생깁니다.

 

 

 

데이터 속성 확인 및 속성 전환 

 

 데이터 속성 확인 및 속성 전환에 대해 알아보기 위해 아래의 텍스트 파일을 불러와서 예를 들어보겠습니다.

 

(실습 파일 아래 다운로드 ☞ ) 

cust_profile_2.txt

 

불러오고 나면 이렇게 생긴 데이터 프레임이 되겠습니다

 

데이터셋의 성격/분석 목적 상 'cust_id'는 character, 'last_name'도 character, 'age'는 numeric, 'gender'는 factor 로 입력되어야 합니다.

그런데 데이터셋이 실제로 어떻게 입력되었는지 2개의 case로 나누어서 확인해보도록 하겠습니다.

 

 

 (1) stringsAsFactor = FALSE   로 한 경우 (요인으로 인식하지 말라는 옵션)

 

> # 외부 텍스트 파일 불러오기 (위에 파일 링크 다운로드 해서 사용 가능)

> cust_profile_1 <- read.table("C:/Users/user/Documents/R/cust_profile_2.txt", + header = TRUE, + sep = ",", + stringsAsFactor = FALSE, + na.strings = "") >

> is.character(cust_profile_1$cust_id) # 문자형 여부 속성 확인 [1] FALSE

> is.character(cust_profile_1$last_name) [1] TRUE > is.numeric(cust_profile_1$age) # 숫자형 여부 속성 확인 [1] FALSE > is.factor(cust_profile_1$gender) # 요인형 여부 속성 확인 [1] FALSE > > sapply(cust_profile_1, class) # 한꺼번에 cust_profile_1 데이터셋에 class() 함수를 적용하라는 명령문 cust_id last_name age gender "integer" "character" "character" "character"

 

 

'last_name'만 character로 원하는 속성으로 들어가 있고, 나머지는 R이 저의 맘을 몰라주고 R 마음대로 데이터 속성이 들어가 있습니다.

'gender'는 남/녀 성별 통계분석이나 그래프 그릴 때 요인(factor)으로 요긴하게 써먹어야 하므로 문자형(character)를 요인형(factor)로

as.factor() 함수를 써서 전해보도록 하겠습니다.

 

> cust_profile_1$gender <- as.factor(cust_profile_1$gender)
> is.factor(cust_profile_1$gender)
[1] TRUE



요인형의 값으로서, 미리 정해진(predefined), 한정된 수의 범주형 값(finite number of categorical values)을 수준(levels) 이라고 합니다. 요인 데이터의 수준을 확인하거나 지정, 변경하려면 levels() 함수를 사용합니다.  


> # level check

> levels(cust_profile_1$gender)

[1] " F" " M"

> # change levels' name from {"F", "M"} to {"FEMALE", "MALE"}

> levels(cust_profile_1$gender) <- c("FEMALE", "MALE")

> levels(cust_profile_1$gender)

[1] "FEMALE" "MALE"  



혹은 factor() 함수labels 옵션을 사용해서 아래처럼 할 수도 있습니다. 



> cust_profile_1$gender <- factor(cust_profile_1$gender, labels = c("FEMALE", "MALE")

 




다음으로, stringsAsFactor = TRUE 로 했을 때는 어떻게 데이터를 불어오는지 알아보겠습니다.

 

 

 (2) stringsAsFactor = TRUE   로 한 경우 (요인으로 인식하라는 옵션)

 

> cust_profile_2 <- read.table("C:/Users/user/Documents/R/cust_profile_2.txt", 
+                              header = TRUE, 
+                              sep = ",", 
+                              stringsAsFactor = TRUE, 
+                              na.strings = "")
> 
> sapply(cust_profile_2, class)
  cust_id last_name       age    gender 
"integer"  "factor"  "factor"  "factor" 
> 
> 
> cust_profile_2$cust_id <- as.character(cust_profile_2$cust_id)
> cust_profile_2$age <- as.numeric(cust_profile_2$age)
> sapply(cust_profile_2, class)
    cust_id   last_name         age      gender 
"character"    "factor"   "numeric"    "factor"

 

 

'gender'는 요인(factor)로 제대로 불러왔는데요, 명목형 변수인 'cust_id'는 문자형(character)가 아닌 숫자형(interger)로, 'last_name'을 문자형(character)가 아닌 요인형(factor)로, 'age'를 숫자형(numeric)이 아닌 요인형(factor)로 잘못 불러왔습니다.

 

자, 이제 아래 표 처럼 이터 특성/분석 목적에 맞게 데이터 속성을 전환 시켜 보도록 하겠습니다.

 

 변수명

 기존 속성

전환 후 속성

 속성 전환 함수

 cust_id

numeric

 character

 as.character()

 last_name

 factor

 character

 as.character()

 age

 factor

 numeric

 as.numeric()

 gender

 factor

 factor

 as.factor()

 

'gender'는 이미 요인형(factor)로 제대로 잘 들어가 있기 때문에 아래 예시에서는 별도로 속성 전환하지는 않았습니다.  

 

> cust_profile_2$cust_id <- as.character(cust_profile_2$cust_id)
> cust_profile_2$last_name <- as.character(cust_profile_2$last_name)
> cust_profile_2$age <- as.numeric(cust_profile_2$age)
>

> sapply(cust_profile_2, class) cust_id last_name age gender "character" "character" "numeric" "factor"

 

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

 

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

 

728x90
반응형
Posted by Rfriend
,