아이티윌_데이터 분석 55기/강의내용 필기_통계 및 분석

#9 9일차_기본 시각화 : barplot, 이상치 탐색, 히스토그램, 커널밀도, 파이차트

ecosso 2026. 4. 10. 16:10

01. 기본 시각화
 -01 plot

  > #8 8일차_dplyr, 시각화

 -02 barplot

 -03 이상치 탐색

 -04 히스토그램

 -05 커널밀도함수

 -06 파이차트


01. 기본 시각화
 -01 plot
 - 선그래프, 산점도
 - 데이터프레임의 교차 산점도
 - 모델 시각화


 -02 barplot

- 범주형 데이터의 비교 시각화(y축: 수치형, x축: 범주형)
barplot(height,             # 데이터
        width = 1,          # 막대 너비
        space = NULL,       # 막대 공간
        names.arg = NULL,   # 막대 이름
        legend.text = NULL, # 범례
        beside = FALSE,     # 서로 다른 막대로의 출력 여부
        horiz = FALSE,      # 수평 여부
        density = NULL,     # 빗금 밀도
        angle = 45,         # 빗금 각도
        border = par("fg"), # 테두리
        ... )

 

# 예) 여러 막대그래프 출력(벡터)
barplot(c(15,60,3,80,100), col = rainbow(5),
        names.arg = c('A','B','C','D','E'),
        density = 30, angle = 130, horiz = T)

 

# 예) 여러 막대그래프 출력(2차원)
library(googleVis)
df1 <- dcast(Fruits, Year ~ Fruit, value.var = 'Sales')

> dcast(Fruits, Year ~ Fruit, value.var = 'Sales')
  Year Apples Bananas Oranges
1 2008     98      85      96
2 2009    111      94      93
3 2010     89      81      98


barplot(df1)

> barplot(df1)
barplot.default(df1)에서 다음과 같은 에러가 발생했습니다: 
  'height'는 반드시 벡터 또는 행렬이어야 합니다

 

df1$Year <- NULL
barplot(as.matrix(df1))

 

barplot(as.matrix(df1), beside = T, col = rainbow(3))

 

barplot(as.matrix(df1), beside = T, col = rainbow(3),
        ylim = c(0, 150),
        cex.axis = 1.5, cex.names = 1.5,
        xlab = 'Fruits', cex.lab = 2, col.lab = 'red')

legend('topright', inset = c(0.03, 0),
       legend = rownames(df1), fill = rainbow(3), horiz = T,
       title = '연도')

 

# [ 연습문제 - barplot 시각화 ]
movie <- read.csv('영화이용현황.csv', fileEncoding = 'cp949')
head(movie)

# 요일별, 연령별 / 이용비율은 총합으로
# 연령은 n0대, 60대 이상은 60대로
# legend는 2x3

더보기

[내 답안]

 

str(movie)
unique(movie$연령대)

# 연령대 정리
movie$연령대 <- str_replace_all(movie$연령대, '\\d$|\\d대 이상', '0대')
movie$연령대 <- str_replace_all(movie$연령대, '70대', '60대')

# 요일 계산
movie$date1 <- str_c(movie$년,movie$월,movie$일, sep = '-')
movie$date1 <- as.Date(movie$date1, '%Y-%m-%d')
movie$day1 <- strftime(movie$date1, '%a')

unique(movie$day1)

# 요일 정렬
movie$day2 <- movie$day1
movie$day2 <- str_replace(movie$day2,'월','1')
movie$day2 <- str_replace(movie$day2,'화','2')
movie$day2 <- str_replace(movie$day2,'수','3')
movie$day2 <- str_replace(movie$day2,'목','4')
movie$day2 <- str_replace(movie$day2,'금','5')
movie$day2 <- str_replace(movie$day2,'토','6')
movie$day2 <- str_replace(movie$day2,'일','7')

unique(movie$day2)

# 필요한 데이터만 추출
movie2 <- ddply(movie, .(day1, day2, 연령대), summarise, 총이용률 = sum(이용_비율...))
movie3 <- arrange(dcast(movie2, 연령대 ~ day1 + day2 ),  day2)
movie4 <- dcast(movie2, 연령대 ~ day2)
head(movie4)


# 요일 라벨링
names(movie4) <- str_replace_all(names(movie4), '1', '월')
names(movie4) <- str_replace_all(names(movie4), '2', '화')
names(movie4) <- str_replace_all(names(movie4), '3', '수')
names(movie4) <- str_replace_all(names(movie4), '4', '목')
names(movie4) <- str_replace_all(names(movie4), '5', '금')
names(movie4) <- str_replace_all(names(movie4), '6', '토')
names(movie4) <- str_replace_all(names(movie4), '7', '일')


# 시각화
my_col3 <- c('#F8F4CB','#FAF34E','#AFFA5A', '#77FA95', '#39FACD', '#5E92FA')


barplot(as.matrix(movie4[2:8]), beside = T, col = my_col3,
        ylim = c(0, 15), xlab = '요일', 
        cex.axis = 1.3, cex.names = 1.3, cex.lab = 1.3)

legend("topright",
       inset = c(0.03, 0.03),
       legend = unique(movie4$연령대),
       fill = my_col3, ncol = 2, title = '연령대')

title('요일별 연령대별 총 이용률 비교', cex.main = 1.5)
mtext('총이용률', side = 2, las = 1, at = 16)
box()

 

 [문제풀이]

 

# 기초 데이터 생성
# 1) 연령대 가공
library(stringr)
v1 <- str_c(str_sub(movie$연령대, 1,1), '0대')
movie$연령대 <- str_replace(v1, '60대|70대', '60대 이상')

# 2) 요일
movie$요일 <- str_c(movie$년, movie$월, movie$일, sep = '/') |> as.Date() |> strftime('%A')

# 3) wide data 생성
library(reshape2)
total <- dcast(movie, 연령대 ~ 요일, sum, value.var = '이용_비율...')
rownames(total) <- total$연령대
total$연령대 <- NULL

# 4) 컬럼 재배치
total[,c(4,7,3,2,1,6,5)]

# 시각화
windowsFonts(
  han = windowsFont("서울한강체 B")
)

par(family = 'han')

library(colorspace)
p1 <- sequential_hcl(6, 'ag_Sunset')


dev.new()
par(bg = '#F8F9F8')
par(family = 'han')

par('mar' = c(6,5,5,3))

barplot(as.matrix(total), beside = T, col = p1, las = 1, ylim = c(0,9),
        main = '요일별 연령대별 영화 이용비율 비교', cex.main = 2.5,
        xlab = '요일', cex.lab = 1.5)

mtext('이용비율(%)', side = 2, at = 8.5, las = 1, cex = 1.2)

legend('topright', inset = c(0.03, 0.08), ncol = 3, bg = '#EFE8E8',
       legend = rownames(total), fill = p1, cex = 0.7,
       title = '연령대', title.cex = 1, title.col = 'red')

 

 


 -03 이상치 탐색

1. 사용자 정의

2. 기법

 1) IQR(범위) : Q3 - Q1

     Range : 최대 - 최소

3. 상자그림

- 수치형 데이터의 분포 비교 시각화
- IQR 기준으로 이상치 출력
- 최대, 최소, 이상치, Q3, Q1, IQR, median(Q2) 확인 가능

 

boxplot(iris[,-5])

 

boxplot(iris[,-5], col = p1[1:4], las = 1,
        main = 'Box Plot', cex.main = 2,
        xlab = 'Species', cex.lab = 1.5,
        cex.axis = 0.8)

 

# 이상치 확인
summary(iris[,-5])
q1 <- quantile(iris$Sepal.Width)[2]
q3 <- quantile(iris$Sepal.Width)[4]
viqr <- q3 - q1
r1 <- iris[iris$Sepal.Width > q3 + 1.5*viqr, ]
r2 <- iris[iris$Sepal.Width < q1 - 1.5*viqr, ]

outlier <- rbind(r1, r2)

> outlier
   Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
16          5.7         4.4          1.5         0.4     setosa
33          5.2         4.1          1.5         0.1     setosa
34          5.5         4.2          1.4         0.2     setosa
61          5.0         2.0          3.5         1.0 versicolor

 

# 이상치 번호 출력
text(2.1, outlier$Sepal.Width, rownames(outlier), cex = 0.7)


 -04 히스토그램

- 도수분포표를 시각화한 그림

- 수치형 데이터에 대한 분포 확인 시각화(계급별로 막대로 표현)

 

hist(x,         # 데이터(벡터)
     breaks,    # 계급구간
     freq = T,  # y축 좌표 -> 도수
     probability = F, # y축 좌표 -> 확률
     include.lowest = TRUE, # 최솟값을 첫번째 계급 포함 여부
     right = T  # 오른쪽 닫힘 여부(a 초과 b 이하)
     )

# 도수분포표
# 140 ~ 150 => 140이상 150미만 [140, 150) : left closed or 140초과 150이하(140, 150] : right closed
# 150 ~ 160
# 160 ~ 170
# ......

 

vheight <- c(140, 142, 150, 155, 158, 162, 168, 169, 170, 171, 177, 176, 177, 183)
hist(vheight)

 

hist(vheight, seq(130, 200, 10), probability = T)


 -05 커널밀도함수

install.packages('ks')
library(ks)

# 화면분할
par(mfrow = c(1,2))

hist(vheight, seq(130, 200, 10), probability = T)
plot(kde(vheight))


 -06 파이차트

- 범주형 자료의 수치를 비교하기 위한 시각화 기법

pie(x,                          # 데이터(벡터)
    labels = names(x),          # 각 파이 이름
    edges = 200,                # 경계선의 부드러움의 정도
    radius = 0.8,               # 반지름
    clockwise = FALSE,          # 데이터의 출력 방향(시계방향 여부)
    init.angle = if(clockwise) 
      
# 예) 기본 파이차트 그리기
v1 <- c(80, 20, 60, 40)
pie(v1,c('A','B','C','D'), col = rainbow(4), clockwise = T,
    radius = 0.6, main = 'Pie Chart')
legend('topright', c('A','B','C','D'), fill = rainbow(4))


 -07 3D 파이차트

install.packages('plotrix')
library(plotrix)

pie3D(x, 
      radius = 1, 
      height = 0.1,# 파이높이 
      start = 0,
      labels = ,
      labelcol = ,
      labelpos = ,
      labelcex = ,
      explode = )  # 파이간격

pie3D(v1, labels = c('A','B','C','D'), col = p1[1:4],
      radius = 1, main = 'Pie Chart', explode = 0.1)

 

 

# [ 연습문제 - 파이차트 시각화 ]
# delivery.csv 파일을 사용하여 
# 업종별 통화건수의 총 비율을 파이차트로 시각화

deli <- read.csv('delivery.csv', fileEncoding = 'cp949')

> head(deli)
      일자 시간대                 업종       시도 시군구   읍면동
1 20180201      0 음식점-족발/보쌈전문 서울특별시 강남구   논현동
2 20180201      0 음식점-족발/보쌈전문 서울특별시 강남구   역삼동
3 20180201      0 음식점-족발/보쌈전문 서울특별시 강서구 내발산동
4 20180201      0 음식점-족발/보쌈전문 서울특별시 강서구   화곡동
5 20180201      0 음식점-족발/보쌈전문 서울특별시 동작구 신대방동
6 20180201      0 음식점-족발/보쌈전문 서울특별시 노원구   상계동
  통화건수
1        5
2        5
3        5
4        5
5        5
6        5

 

# 기초 데이터 생성
library(plyr)
deli2 <- ddply(deli, .(업종), summarise, 총통화건수 = sum(통화건수))
total <- deli2$총통화건수
names(total) <- str_remove(deli2$업종, '음식점-')
vnames <- str_c(round(total/sum(total) * 100, 2), '%')

> head(total)
족발/보쌈전문      중국음식          치킨          피자 
        92191        438453        474925        176367 


# 시각화                           
dev.new()
pie3D(total, col = p1[1:4], explode = 0.1,
      main = '업종별 배달현황', cex.main = 2,
      labels = vnames, labelcex = 1.2)

legend('topright', names(total), fill = p1[1:4], cex = 0.9,
       title = '업종', title.col = 'red')