study blog

dataframe 조작 - R내장 함수, plyr, dplyr 본문

R/3. 데이터 조작

dataframe 조작 - R내장 함수, plyr, dplyr

ivo_lee 2020. 1. 14. 17:42

데이터 분석업무에서 raw data를 얻은 다음 머신러닝 모델링을 위해서 또는 시각화를 위해서 raw data를 적절한 형태로 변형

데이터 변환, 필터링, 전처리 작업이 필요!

데이터 조작에 특화된 package들이 존재!

- plyr : pliers(집게)+R  (플라이어, 플라이 알이라고 부름)

- dplyr : data frame + pliers + R (디플라이알)

- vector data frame에 적용할 수 있는 기본 함수

 

1. R 내장 함수로 데이터 조작하기

# iris 데이터를 이용하여 데이터를 조작해보자.

- iris: 붓꽃의 종류와 크기에 대해 측정한 데이터 - 통계학자 피셔가 측정해서 제공

 

ls():  data frame column명을 vector로 추출, 오름차순으로 정렬 

- Petal.Length : 꽃잎의 길이          

- Petal.Width : 꽃잎의 너비

- Sepal.Length : 꽃받침의 길이       

- Sepal.Width: 꽃받침의 너비     

- Species: (3가지)

ls(iris)

head(): 데이터셋의 앞에서부터 6개 데이터 추출  - 데이터프레임이 아닌 경우도 적용 가능 

    tail(): 데이터셋의 뒤에서부터 6개 데이터 추출   - 데이터프레임이 아닌 경우도 적용 가능

head(iris,3)   # 앞에서 3개 추출
head(iris,-148)  #  뒤에서부터 148개 빼고 추출

tail(iris)   # 뒤에서 6개 추출
tail(iris,-149)   # 앞에서부터 149개 빼고 추출

③ View(): View 창에 데이터를 출력

View 창에서 filter 누르면 분포가 나옴필터링해서 볼 수 있음 !!!!

 

dim(): 차원 구하기. data frame에 적용할 때 행과 열의 개수를 알려줌

dim(iris)  # [1] 150   5   :150행 5열

선형 자료구조(1차원-vector,list)에서는 사용 불가

dim(1:6)  #NULL

nrow() : data frame의 행의 개수

    ncol() : data frame의 열의 개수

nrow(iris)  # 150
ncol(iris)  # 5

str() : data frame의 일반적인 정보를 추출

str(iris)

summary() : data frame의 요약통계량을 보여줌

summary(iris)

- min, max, mean: 평균, median: 중간값

- 1st Qu. : 밑에서 1/4    3rd Qu.: 밑에서 3/4

 

rev() : reverse.선형자료구조 데이터의 순서를 역순으로 만듦

rev(1:6)       #[1] 6 5 4 3 2 1
rev(ls(iris))

length() : 길이를 구하는 함수

data frame length를 구하면 column의 개수를 구해줌

주의! matrix에서 length 쓰면 matrix전체의 길이 (2차원자료구조지만 matrix의 length는 요소의 개수)

var1 <- matrix(1:12,ncol=3)
length(var1) 

2. plyr 이용해서 데이터 조작하기

- plyr 설치하기

install.packages("plyr")
library(plyr)

Join() : key 값을 이용해서 2개의 data frame 병합(세로방향, 열방향으로 결합)

(1) 1개의 key를 이용해서 결합하는 법

x <- data.frame(id=c(1,2,3,4,5),
                height=c(150,190,170,188,167))
y <- data.frame(id=c(1,2,3,6),
                weight=c(50,100,80,78))

join(x,y,by="id",type="inner")
join(x,y,by="id",type="left")    #default
join(x,y,by="id",type="right")
join(x,y,by="id",type="full")

(2) key 2개 이상 이용해서 결합하는 법

x <- data.frame(id=c(1,2,3,4,5),
                gender=c("M","F","M","F","M"),
                height=c(150,190,170,188,167))

y <- data.frame(id=c(1,2,3,6),
                gender=c("F","F","M","F"),
                weight=c(50,100,80,78))

join(x,y,by=c("id","gender"),type="inner")
# 출력결과
      id  gender  height  weight
  1   2      F     190      100
  2   3      M     170       80

# 1의 gender 일치하지 않으므로 결과로 나오지 않음

 

unique(): 중복 제거

unique(iris$Species) 

# 출력결과
[1] setosa     versicolor virginica
Levels: setosa versicolor virginica

컬럼수 작을경우는 str사용해도 괜찮지만 수 많은 경우 unique 사용하는 것이 좋다

 

tapply() : 범주형 변수를 이용해서 그룹별 통계량 구하기

 tapply(대상 column, 범주형 column(나눌 기준), 적용할 함수)

 tapply의 단점: 한 번에 1개의 통계만 구할 수 있음

 2개의 통계 같이 구하고 싶을 때

 

[연습문제] iris의 종별 꽃잎 길이의 평균을 구하시오.

tapply(iris$Sepal.Length,  iris$Species, FUN=mean)

 

ddply() : tapply와 같은 기능. but 한번에 여러개의 통계치 구할 수 있음

 

[연습문제] iris의 종별 꽃잎 길이의 평균과 표준편차를 구하시오.

df = ddply(iris,                 # 대상 데이터 셋 지정
            .(Species),          # 기준 (column명)
            summarise,           # summarise 함수 : 여러 기본통계량 다 뽑아냄
            avg=mean(Petal.Length),
            sd = sd(Petal.Length))

3. dplyr 이용해서 데이터 조작하기

- data frame handling할 때는 plyr대신 dplyr을 이용하는 경우가 많음.

- dplyr c++로 구현되었기 때문에 속도가 빠름!!!

- dplyr은 코딩시 chaining을 사용할 수 있음

 

 

(+) chaining이란? 

var1 <- c(1,2,3,4,5)

var2 <- var1 * 2

var3 <- var2 + 5

이러한 과정을 var1 >> *2  >> +5 로 중간변수 안만들고 바로 하겠다

 

tbl_df(): 현재 console 크기에 맞춰 data frame을 추출하는 함수(데이터 확인 함수)

head(iris)     # 내용 많으면 아래에 떨어짐.. 콘솔창과 폰트에 따라 다름
tbl_df(iris)   # 현재 콘솔창 크기에 맞춰서 보여줄 수 있는 거만 보여줌 (넘치는 나머지 칼럼은 안보여줌)

 

rename(): data frame column명 변경

rename(data frame, 바꿀 컬럼명1=이전 컬럼명1, 바꿀 컬럼명2=이전 컬럼명2)

 

# 제공된 excel 데이터 파일을 이용

install.packages("xlsx")
library(xlsx)
excel <- read.xlsx(file.choose(),           # file.choose: 대화상자로 내가 선택할 수 있음
                   sheetIndex = 1,
                   encoding="UTF-8")

str(excel);   ls(excel)

# AMT17 : 17년 이용금액(amount)
# Y17_CNT : 17년 이용횟수(count)   -> 두가지를 하나로 통일해주면 좋을 듯
# column명을 수정한 새로운 data frame 리턴 (주의! 기존 데이터가 바뀌는 것은 아님)

df <- rename(excel, CNT17=Y17_CNT, CNT16=Y16_CNT)

 

filter() : 하나의 data frame에서 하나 이상의 조건을 이용해서 데이터를 추출하려면?

filter(data frame, 조건1, 조건2, ...)

filter(excel, SEX=="M", AREA == "서울")
filter(excel, SEX=="M"& AREA == "서울")  #이렇게 써도 상관없음

 

[연습문제] 지역이 서울 혹은 경기인 남성들 중 40살 이상인 사람들의 정보 출력

filter(excel, AREA =="서울"|AREA =="경기", SEX=="M", AGE>=40 )

[연습문제] 지역이 서울 혹은 경기 혹은 제주인 남성들 중 40살 이상인 사람들의 정보 출력

filter(excel,
       AREA %in% c("서울","경기","제주"),      #  %in%: ~~안에 포함된
       SEX=="M",
       AGE>=40 )

 

④ arrange() : 정렬

arrange(data frame, column1, column2, ...)     

정렬기준 column1, 동률 있으면 column2, ...  ( default: 오름차순, desc(column) : 내림차순 정렬 )

 

[연습문제] 서울, 남자, 2017년도 처리금액이 400,000원 이상인 사람을 나이가 많은 순으로 출력

my_excel <- filter(excel, AREA =="서울", SEX =="M", AMT17 >=400000)
arrange(my_excel, desc(AGE))

 

(+) Chaining

- dplyr chaining이 가능  : %>%  (chaining 기호)

my_excel <- filter(excel,
                   AREA =="서울",
                   SEX =="M",
                   AMT17 >=400000) %>%
arrange(desc(AGE))    # 앞의 data frame 받으므로 조건만 쓰면 됨

select() : 추출하고 싶은 column 지정해서 해당 column만 추출 가능

-  filter record 선택, select는 열 선택

select(data frame, column1, column2, ...)

 

[연습문제] 서울, 남자, 2017 처리금액이 400,000 이상인 사람 나이가 많은 순으로 ID, 나이, 2017년 처리 건수만 출력

filter(excel,
       AREA =="서울",
       SEX =="M",
       AMT17 >=400000)  %>%
  arrange(desc(AGE))  %>%
  select(ID,AGE,Y17_CNT)

(+) 다른 방식으로도 select할 수 있음

select("ID":"AGE")  #이런식으로 범위를 넣어도 상관없음
select(-SEX)      #성별 빼고 다 출력
select(1,3,6) # 칼럼 숫자로도 출력가능 (ex)1:4

 

 mutate() : 새로운 column을 생성할 수 있음 (없는 column을 연산을 통해 생성 가능!)

mutate(data frame, 컬럼명1 = 수식1, 컬럼명2 = 수식2)   # 수식이 아니라 값으로 줘도 됨

 

[연습문제] AMT17 >=500000인 사람을 VIP, 나머지를 Normal으로 'GRADE'라는 새로운 column 생성하시오.

(R의 기본 기능 이용)

- data frame column을 생성하는 기본기능 (R의 기본기능)

excel$GRADE = "VIP"    # 새로운 컬럼 생성 -> 다 vip로 생성됨
excel$GRADE = ifelse(excel$AMT17>=500000,"VIP","NORMAL")
# = excel$GRADE[excel$AMT17 >=500000] ="NORMAL"

 

[연습문제] (dplyr 패키지를 이용하여)

경기사는 여자를 기준으로

17년도 처리금액을 이용하여 처리금액의 10%를 가산한 값으로 새로운 컬럼 AMT17_REAL 만들고, 

AMT17_REAL 45만원 이상인 경우 VIP 컬럼 만들어서 TRUE, FALSE를 입력해라.

excel <- read.xlsx(file.choose(),  # file.choose: 대화상자로 내가 선택할 수 있음
                   sheetIndex = 1,
                   encoding="UTF-8")
filter(excel, AREA=="경기" & SEX=="F") %>%
  mutate(AMT17_REAL=AMT17*1.1, VIP=ifelse(AMT17_REAL>=450000,T,F))

 

 group_by() & summarise()

df <- filter(excel, AREA=="서울" & AGE>30) %>%
      group_by(SEX) %>%                    # 성별로 grouping
      summarise(sum=sum(AMT17),           # 성별대로 행 만든 다음 열의 sum에는 AMT17합을,
                cnt=n())                     # cnt에는 개수 넣음

 

 join() : plyr package join함수가 각 기능별로 독립적인 함수로 제공됨

left_join()

right_join()

inner_join()

full_join()

 

bind_rows() : 데이터프레임을 행단위(가로로) 붙임

bind_rows(df1,df2)

 

※ 주의: 컬럼명이 같아야 생각하는대로 데이터프레임이 결합됨 - 컬럼명이 같지 않으면 컬럼을 생성해서 결합됨

df1 <-data.frame(x=c("a","b","c"))
df2 <-data.frame(x=c("d","e","f"))
bind_rows(df1,df2)

df3 <-data.frame(y=c("d","e","f"))
bind_rows(df1,df3)

 

 


4. 데이터 조작 연습문제

MovieLens Data Set을 이용해서 처리해보자!

- 영화에 대한 평점 정보를 기록해 놓은 데이터

- 평점은 1~5(한 사람이 여러 영화에 대해 점수 줄 수 있음)

- 구글에서 MovieLens 검색 -> recommended for education and development 에서 1mb짜리 다운받기

 

 

(+) timestamp: 숫자로 날짜를 표현하는 방식

timestamp 날짜
1 1970 1 1 0 0 1
2 1970 1 1 0 0 2

하루 뒤: +60*60*24

 

 

# 데이터 불러오기

setwd("C:/Users/student/Downloads/ml-latest-small/ml-latest-small")
ratings <- read.csv("ratings.csv"); movies <- read.csv("movies.csv")
#movies <- read.csv(file.choose());  ratings <- read.csv(file.choose())

 

[연습문제 1] 사용자가 평가한 모든 영화의 전체 평균 평점

total_rating <- mean(ratings$rating)

 

 [연습문제 2] 각 사용자별 평균 평점

#dplyr을 이용한 방법
user_rating <- group_by(ratings, userId) %>%
                summarise(user_mean=mean(rating))

#plyr을 이용한 방법
tapply(ratings$rating,
       ratings$userId,
       FUN=mean)

 

[연습문제 3] 각 영화별 평균 평점

#dplyr을 이용한 방법
movie_rating <- group_by(ratings, movieId) %>%
                summarise(movie_mean=mean(rating))

#plyr을 이용한 방법
tapply(ratings$rating,
       ratings$movieId,
       FUN=mean)

 

[연습문제 4] 평균 평점이 가장 높은 영화의 제목을 내림차순으로 정렬해서 출력 (동률은 모두 출력)

group_by(ratings, movieId) %>%
  summarise(movie_mean=mean(rating)) %>%
  filter(movie_mean==max(movie_mean)) %>%
  right_join(movies,by="movieId") %>%
  select(title) %>%
  arrange(desc(title))

 

[연습문제 5] comedy 영화 중 가장 평점이 낮은 영화의 제목을 오름차순으로 출력 (동률은 모두 출력)

inner_join(movies,ratings,by="movieId") %>%
  filter(grepl('Comedy', genres)) %>%
  group_by(title) %>%
  summarise(mr=mean(rating)) %>%
  filter(mr==min(mr)) %>%
  select(title)

 

[연습문제 6] 2015년도에 평가된 모든 Romance 영화의 평균 평점 출력

library(stringr)

romance_rating <- filter(ratings,
       str_sub(as.POSIXct(timestamp, origin="1970-01-01"),1,4)==2015) %>%
  left_join(movies,by="movieId") %>%
  filter(grepl('Romance', genres)) %>%
  select(rating)

romance_rating <- inner_join(movies,ratings,by="movieId") %>%
  filter(str_sub(as.POSIXct(timestamp, origin="1970-01-01"),1,4)==2015,
         grepl('Romance', genres)) %>%
  select(rating)
mean(romance_rating$rating)

# 로맨스 영화별 평균평점
inner_join(movies,ratings,by="movieId") %>%
  filter(str_sub(as.POSIXct(timestamp, origin="1970-01-01"),1,4)==2015,
         grepl('Romance', genres)) %>%
  group_by(title) %>%
  summarise(romance_mean=mean(rating)) 

 


5. mpg 데이터를 이용하여 실습해보자. (복습)

 

- package 함수를 이용한 dataframe 조작( plyr, dplyr, reshape2)

- mpg data set을 이용해서 데이터 조작, 정제에 대한 내용을 학습해보자.

str(mpg)       # 자료구조를 조사해보자! - 전체적인 정보
class(mpg)     # 자료구조만 알려줌      # mpg는 table data frame 형태
df <- as.data.frame(mpg)                 # data frame으로 변환  

 

- data frame column명을 알아보자.

ls(df)  # column명을 오름차순 정렬해서 추출
help(mpg)

# displ: 배기량

# cty: 도시연비

# hwy: 고속도로연비

# fl: p:premium , c: compact(소형차)

 

View(df)  #View창을 통해 데이터를 확인
dim(df)   #data frame에서는 행, 열의 수를 알려줌
nrow(df)  #행의 개수
ncol(df)  #열의 개수
length(df) #열의 개수

※ 원래 length()는 원소의 개수를 구하는 함수인데 data frame에서는 column의 개수를 구함

 

str(df)   # 자료구조, 행의 개수, 열의 개수, 컬럼명, 데이터 타입, ...
summary(df)  # 가장 기본적인 통계 데이터를 추출
rev()   # vector에 대해서 데이터를 역순으로 변환하는 기능

 

# 데이터 조작 ( dplyr : 디플라이알 )

install.packages("dplyr")
library(dplyr)

 

 

tbl_df()

df <- tbl_df(df)          #table data frame
df <- as.data.frame(df)   # data frame

 

rename() : column의 이름을 변경할 수 있음

rename(df,새로운 컬럼 = 원래컬럼)

- raw data를 이용할 경우 column명이 없을 때 column명을 새로 명시해서 사용해야 함

- 컬럼명에 대소문자가 같이 있는 경우 모두 소문자, 대문자로 변경해서 사용하면 편함

- df의 컬럼명을 모두 소문자 혹은 대문자로 변경

# rename으로 하나만 바꾸기
new_df<-rename(df, MODEL = model)  #model -> MODEL   ;    head(new_df)

# names로 다 바꾸기
names(df) = toupper(ls(df))  #base package에 있는 함수: 대문자로 바꿔줌

 

filter(): 조건을 만족하는 행을 추출하는 함수

filter(data frame, 조건1, 조건2, 조건3,...)

 

[연습문제] 2008년도에 생산된 차량이 몇 개 있는지 추출

df <- as.data.frame(mpg)
nrow(filter(df, year==2008))   #117개

[연습문제] 모든 차량에 대해 평균 도시연비보다 도시연비가 높은 차량의 model명 출력

myDf = filter(df,  cty>mean(cty)) %>%
            select(model)
nameDf<- unique(myDf)
#row.names(table(myDf))
nrow(nameDf)    # 높은 차량의 개수 : 23개 // 높은 전체 차량의 개수: 118개

avg_cty <-  mean(df$cty,na.rm=T) # 평균 도시연비

# 주의 ! NA 가 들어있으면 결과값도 이상한 값이 나올 것 ! -> na.rm=T

 

[연습문제] 고속도로 연비가 상위 75% 이상인 차량을 제조하는 제조사는 몇 개인지 추출하시오.

length(unique(filter(df, hwy >= summary(df$hwy)[5])$manufacturer))   #3사분위수 : quantile(hwy,0.75)

[연습문제] 오토 차량중 2500cc 이상인 차량 수는 몇개인가?

install.packages("stringr")
library(stringr)

nrow( filter(df, str_detect(trans,"auto"),      #str_sub(trans,1,4)=="auto" # grepl("auto",trans)
             displ >= 2.5) )                # 125

 

arrange(): 정렬하는 함수

arrange(data frame,  column1, desc(column2) )    # 기본 정렬방식: 오름차순

avg_cty <-  mean(df$cty,na.rm=T)
unique(filter(df, cty > avg_cty)$model)

[연습문제] 모든 차량에 대해 평균 도시연비보다 도시연비가 높은 차량의 model명을 출력하는데 모델명을 오름차순으로 정렬

unique( df %>% filter(cty > avg_cty) %>%
               select(model) %>%
               arrange(model) )

df %>% filter(cty > avg_cty) %>%
       select(model) %>%
       unique() %>%
       arrange(model)
       
sort(unique(filter(df,
                   cty > avg_cty)$model))

 

select() : data frame에서 원하는 column만 추출하는 함수

select(data frame, column1, column2,...)

 

mutate(): 새로운 column을 생성하려면 ?

 

[연습문제] 도시연비와 고속도로 연비를 합쳐서 평균 연비 column을 만들어 보자.

# 전통적인 column 생성 방법: 기본 R의 기능을 이용해서 column을 만들 수 있음
df$mean_rate=(df$cty+df$hwy)/2

# dplyr 활용
df <- as.data.frame(mpg)
df %>% mutate(mean_rate=(cty+hwy)/2)

 

summarise(): 통계량을 구해서 새로운 컬럼으로 생성하는 함수

summarise(df,my = mean(cty))

 

[연습문제] model명이 a4이고 배기량이 2000cc이상인 차들에 대해 평균 연비를 계산해라.

## 답 1
result <- df %>%
          filter(model=="a4"&displ >= 2.0) %>%
          mutate(avg_rate = (cty+hwy)/2)
mean(result$avg_rate)

## 답 2 -> summarise를 이용해보자
df %>% filter(model=="a4"&displ >= 2.0) %>%
  summarise(avg_rate = mean(c(cty,hwy)),
            haha = max(cty))    
            # -> vector를 대상으로 mean 수행.  mean(cty,hwy)는 안됨

 

group_by(): 범주형 변수에 대한 grouping

df %>% filter(displ >= 2.0) %>%
  group_by(manufacturer) %>%
  summarise(avg_rate = mean(c(cty,hwy)))

 

left_join(), right_join(),inner_join(),outer_join()

 


 

[연습문제] mpg data set에 대해서 다음의 내용을 수행하시오.

df <- as.data.frame(mpg)

 

[연습문제 1] displ(배기량) 4 이하인 자동차와 5 이상인 자동차 중 어떤 자동차의 hwy(고속도로 연비)가 평균적으로 더 높은지 확인하세요.

#  4 이하  5 이상
#     11      12

avg1 <- df %>% filter(displ<=4) %>%
  summarise(avg_4 = mean(hwy))
avg2 <- df %>% filter(displ>=5) %>%
  summarise(avg_5 = mean(hwy))
cbind("4 이하"=avg1$avg_4,"5 이상"=avg2$avg_5)

result <- df %>% filter(displ<=4|displ>=5) %>%
       group_by(displ>=5) %>%
       summarise(avg_hwy = mean(hwy))
result2 <- result$avg_hwy
names(result2)=c("4 이하","5 이상")

df %>% summarise("4이하"=mean((df %>% filter(displ<=4))$hwy),
                 "5이상"=mean((df %>% filter(displ>=5))$hwy))

 

[연습문제 2] 자동차 제조 회사에 따라 도시 연비가 다른지 알아보려고 한다. "audi" "toyota" 중 어느 manufacturer(제조회사) cty(도시 연비)가 평균적으로 더 높은지 확인하세요.

df %>%
  filter(manufacturer %in% c("audi","toyota")) %>%
  group_by(manufacturer) %>%
  summarise(avg_cty = mean(cty))

 

[연습문제 3] "chevrolet", "ford", "honda" 자동차의 고속도로 연비 평균을 알아보려고 한다. 이 회사들의 데이터를 추출한 후 hwy(고속도로 연비) 전체 평균을 구하세요.

df %>% filter(manufacturer %in% c("chevrolet", "ford", "honda")) %>%
  summarise(avg_hwy = mean(hwy))

 

[연습문제 4] "audi"에서 생산한 자동차 중에 어떤 자동차 모델의 hwy(고속도로 연비)가 높은지 알아보려고 한다. "audi"에서 생산한 자동차 중 hwy 1~5위에 해당하는 자동차의 데이터를 출력하세요.

result <- df %>% filter(manufacturer=="audi") %>%
  arrange(desc(hwy))
head(result,5)

 

[연습문제 5] mpg 데이터는 연비를 나타내는 변수가 2개입니다. 두 변수를 각각 활용하는 대신 하나의 통합 연비 변수를 만들어 사용하려 합니다. 평균 연비 변수는 두 연비(고속도로와 도시)의 평균을 이용합니다. 회사별로 "suv" 자동차의 평균 연비를 구한후 내림차순으로 정렬한 후 1~5위까지 데이터를 출력하세요.

result <- df %>% filter(class=="suv") %>%
       group_by(manufacturer) %>%
       summarise(avg_rate = mean(c(cty,hwy))) %>%
       arrange(desc(avg_rate))
head(result,5)

 

[연습문제 6] mpg 데이터의 class "suv", "compact" 등 자동차의 특징에 따라 일곱 종류로 분류한 변수입니다. 어떤 차종의 도시 연비가 높은지 비교하려 합니다. class cty 평균을 구하고 cty 평균이 높은 순으로 정렬해 출력하세요.

df %>% group_by(class) %>%
       summarise(avg_cty = mean(cty))
       arrange(desc(avg_cty))

 

[연습문제 7] 어떤 회사 자동차의 hwy(고속도로 연비)가 가장 높은지 알아보려 합니다. hwy(고속도로 연비) 평균이 가장 높은 회사 세 곳을 출력하세요.

result <- df %>% group_by(manufacturer) %>%
       summarise(avg_hwy = mean(hwy)) %>%
       arrange(desc(avg_hwy))
head(result,3)$manufacturer

 

[연습문제 8] 어떤 회사에서 "compact" 차종을 가장 많이 생산하는지 알아보려고 합니다. 각 회사별 "compact" 차종 수를 내림차순으로 정렬해 출력하세요.

df %>% filter(class=="compact") %>%
       group_by(manufacturer) %>%
       summarise(cc=n()) %>%
       arrange(desc(cc))
Comments