聰明不如鈍筆
총명불여둔필

MS 엑셀, R, 파이선에서 홀수 행 또는 짝수 행만 선택하기


데이터를 다루다 보면 여러 가지 이유로 홀수 행 또는 짝수 행만 골라야 할 때가 있습니다.


물론 꼭 홀수, 짝수가 아니라 3행, 6행, 9행…처럼 특정 숫자 배수인 행만 필요할 때도 있습니다.


어떻게 하면 이런 행만 쉽게 골라낼 수 있을까요?


실마리는 '나머지'에 있습니다.


어떤 숫자를 2로 나눴을 때 홀수는 나머지가 1이지만 짝수는 나머지가 없습니다(0).


마찬가지로 어떤 숫자를 3으로 나눴을 때 나머지가 0인 숫자가 바로 3의 배수입니다.


실제로 이 작업을 어떻게 하는지 데이터 작업에 가장 많이 쓰는 프로그램 세 개 - 마이크로소프트(MS) 엑셀, R 그리고 파이선에서 직접 실습을 해보겠습니다.


엑셀이 궁금하신 분은 바로 스크롤하시면 되고 R이 필요하시면 여기를, 파이선이 필요하시면 여기를 클릭(또는 터치)하시면 됩니다.



엑셀에서 짝수 행 또는 홀수 행만 골라내기


먼저 아래 같은 연습용 데이터를 만들었습니다.


A열(x)에는 1~10을 넣었고 B열(y)에는 그냥 RAND() 함수로 난수를 발생해 넣었습니다.


그러니까 B열은 실제로 별로 중요한 내용이 아닙니다. 그냥 행 번호 말고도 자료가 있다고 가정을 한 겁니다.



그다음 C열에 그룹(g)을 나타내는 변수를 추가해 보겠습니다.


위에서 우리는 나머지로 그룹을 나눌 것이라고 했습니다. 엑셀에서 나머지를 계산하는 함수는 MOD()입니다.



짝수, 홀수를 구분하려면 x열에 있는 열번호를 2로 나눠야겠죠?


그러려면 C2 셀에 =MOD(A2, 2)라고 입력하면 됩니다.



이어서 C3~C11 셀에도 같은 코드를 붙여 넣으면 아래처럼 나옵니다.



잘 나왔습니다. 이제 홀수 행이 필요하시면 g가 1인 행만 골라내시면 되고 짝수일 때는 0만 뽑으시면 됩니다.


방법이 여러 가지 있겠지만 '데이터 - 필터'를 쓰시면 간단하게 필요한 값만 고르실 수 있습니다.



그런데 작업을 이렇게 하려면 꼭 행 번호가 나오는 열 하나가 추가로 필요합니다.


ROW() 함수를 쓰시면 이런 열이 없어도 됩니다.



단, 지금 이 데이터에서는 맨 첫 줄에 열 이름이 나와 있다는 점을 주의하셔야 합니다.


이때는 ROW() 결과에서 1을 빼야 진짜 행 번호가 나옵니다.


3의 배수인 행만 골라내고 싶으면 =MOD(ROW()-1, 3)이라고 쓰면 됩니다.



마찬가지로 C11 셀까지 수식을 붙여 넣으면 예상하셨던 것처럼 결과가 잘 나왔을 겁니다.



이제 위와 마찬가지로 필터로 g가 0인 행만 골라내면 그만입니다.


또 C열을 기준으로 데이터를 오름차순 정렬하셔도 3의 배수인 행만 골라낼 수 있습니다.



R에서 짝수 행, 홀수 행만 골라내기


'R로 스도쿠 퍼즐을 풀어보자(feat. function(function(x)))' 포스트에서 말씀드렸던 것처럼 R에서 나머지를 구할 때는 %% 기호를 씁니다.


실제로 1과 2를 2로 나눠 보면 아래처럼 나옵니다.

1 %% 2
## [1] 1
2 %% 2
## [1] 0


나머지를 구하는 법을 알게 됐으니 일단 엑셀 때와 똑같이 연습용 데이터를 만듭니다.

df <- read.csv(textConnection(
  "x,y
  1,0.4551
  2,0.5376
  3,0.1322
  4,0.4447
  5,0.3112
  6,0.7853
  7,0.0035
  8,0.6910
  9,0.9812
  10,0.0473"
  ))
df
##     x      y
## 1   1 0.4551
## 2   2 0.5376
## 3   3 0.1322
## 4   4 0.4447
## 5   5 0.3112
## 6   6 0.7853
## 7   7 0.0035
## 8   8 0.6910
## 9   9 0.9812
## 10 10 0.0473


R에서 특정 열을 골라낼 때는 데이터 프레임(또는 티블) 이름을 쓰고 대괄호([ ]) 안에 조건을 써주면 됩니다.


짝수 행만 골라낼 때는 행 번호(여기서는 df$x)를 2로 나눴을 때 나머지가 0인 행만 출력하라고 코드를 쓰면 됩니다.


실제 코드는 이렇게 씁니다. (R에서 같다고 표시할 때는 '='가 아니라 '=='라고 씁니다.)

df[df$x%%2==0,]
##     x      y
## 2   2 0.5376
## 4   4 0.4447
## 6   6 0.7853
## 8   8 0.6910
## 10 10 0.0473


여기서 조건을 쉼표로 마무리한 건 행을 골라내기 때문에 그렇습니다.


만약 그냥 [ ]로 마무리하면 열을 골라냅니다.


같은 원리로 얼마든 홀수 행이나 n의 배수 행도 골라낼 수 있습니다. 


한 걸음 더 나가 보겠습니다.


이 블로그를 꾸준히 읽어 오시면 분이라면 R를 다룰 때는 tidyverse를 쓰면 훨씬 편한 걸 알고 계실 겁니다. 


그래서 일단 tidyverse 패키지를 (설치하고) 불러옵니다.

#install.packages('tidyverse')
library('tidyverse')
## -- Attaching packages --------------------------------------------------------------------------------- tidyverse 1.3.0 --
## √ ggplot2 3.3.0     √ purrr   0.3.3
## √ tibble  2.1.3     √ dplyr   0.8.4
## √ tidyr   1.0.2     √ stringr 1.4.0
## √ readr   1.3.1     √ forcats 0.5.0
## -- Conflicts ------------------------------------------------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()


이제 dplyr 문법으로 같은 작업을 진해할 겁니다.


혹시 dplyr가 낯선 분이 계시면 '최대한 친절하게 쓴 R로 데이터 뽑아내기(feat. dplyr)' 포스트가 도움이 될 수 있습니다.


행 번호를 담고 있는 x를 2로 나눴을 때 나머지를 담고 있는 g열을 만들어 보겠습니다.

df %>% 
  mutate(g=x%%2)
##     x      y g
## 1   1 0.4551 1
## 2   2 0.5376 0
## 3   3 0.1322 1
## 4   4 0.4447 0
## 5   5 0.3112 1
## 6   6 0.7853 0
## 7   7 0.0035 1
## 8   8 0.6910 0
## 9   9 0.9812 1
## 10 10 0.0473 0


이제 g가 1인 열만 골라내면 홀수 행만 뽑을 수 있습니다.

df %>% 
  mutate(g=row_number()%%2) %>%
  filter(g==1)
##   x      y g
## 1 1 0.4551 1
## 2 3 0.1322 1
## 3 5 0.3112 1
## 4 7 0.0035 1
## 5 9 0.9812 1


이번에도 엑셀 때와 마찬가지로 행 번호를 담고 있는 열이 따로 있어야 한다는 문제가 있습니다.


행 번호를 출력하는 row_number() 함수를 쓰면 이 문제를 해결할 수 있습니다.


3의 배수 행만 골라내는 코드는 이렇게 쓸 수 있습니다.

df %>% 
  mutate(g=row_number()%%3) %>%
  filter(g==0)
##   x      y g
## 1 3 0.1322 0
## 2 6 0.7853 0
## 3 9 0.9812 0


간단하고 깔끔하게 원하는 결과가 나왔습니다.



파이선에서 짝수 행 또는 홀수 행만 골라내기


엑셀에서는 MOD(), R에서는 %%가 담당하는 구실을 파이선에서는 %가 책임집니다.


그러니까 파이선에서 나머지를 구할 때는 % 기호를 한 번만 쓰면 됩니다.


1, 2를 2로 나눴을 때 나머지를 출력하라는 코드는 아래처럼 쓸 수 있습니다.

print(1 % 2, 2 % 2)
1 0


이어서 판다스(pandas) 패키지를 불러오고 연습용 데이터를 만드는 과정까지 진행해 보겠습니다.

import pandas as pd


아, 혹시 pandas 패키지를 설치하지 않으셨다면 PIP(Python Packages Index)에서 설치부터 하셔야 합니다.


운영체제(OS) 쉘에서 (마이크로소프트·MS 윈도라면 커맨드 창)에서 다음 명령어를 입력하시면 됩니다.

pip install pandas


그럼 계속해서 연습용 데이터를 만들겠습니다.

data = {'x':[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
        'y':[0.4551, 0.5376, 0.1322, 0.4447, 0.3112, 0.7853, 0.0035, 0.6910, 0.9812, 0.0473]}
df = pd.DataFrame(data)


이 데이터 프레임을 출력해 보면 이렇게 생겼습니다.

print(df)
    x       y
0   1  0.4551
1   2  0.5376
2   3  0.1322
3   4  0.4447
4   5  0.3112
5   6  0.7853
6   7  0.0035
7   8  0.6910
8   9  0.9812
9  10  0.0473


계속해 이 데이터 프레임에 x열을 2로 나눴을 때 나머지를 담고 있는 g열을 추가합니다.


dplyr에서 mutate()로 (계산) 열을 추가하는 것처럼 판다스에서는 assign() 메서드를 씁니다.

print(df.assign(g=df.x % 2))
    x       y  g
0   1  0.4551  1
1   2  0.5376  0
2   3  0.1322  1
3   4  0.4447  0
4   5  0.3112  1
5   6  0.7853  0
6   7  0.0035  1
7   8  0.6910  0
8   9  0.9812  1
9  10  0.0473  0


짝수 행만 출력하고 싶으면 g가 0인 행만 골라면 되겠죠?


dplyr에서 filter()를 쓰는 것처럼 판다스에서는 query()를 써서 원하는 행만 고를 수가 있습니다.


위에 있는 코드하고 합치면 이렇게 쓸 수 있습니다.

print(df.assign(g=df.x % 2).query('g==0'))
    x       y  g
1   2  0.5376  0
3   4  0.4447  0
5   6  0.7853  0
7   8  0.6910  0
9  10  0.0473  0


이번에도 굳이 x열이 필요한 건 아닙니다. 


판다스에서는 index()로 행 번호를 붙일 수가 있습니다.

print(df.index)
    x       y  g
0   1  0.4551  1
1   2  0.5376  0
2   3  0.1322  1
3   4  0.4447  0
4   5  0.3112  1
5   6  0.7853  0
6   7  0.0035  1
7   8  0.6910  0
8   9  0.9812  1
9  10  0.0473  0
RangeIndex(start=0, stop=10, step=1)


이때 주의하셔야 하는 건 엑셀이나 R와는 달리 파이선은 행 번호를 (사실은 모든 숫자가) 1이 아니라 0부터 시작한다는 점입니다.


그래서 index를 2로 나눈 나머지가 0인 행을 출력하라고 하면 홀수 행이 나옵니다.

print(df.assign(g=df.index % 2).query('g==0'))
   x       y  g
0  1  0.4551  0
2  3  0.1322  0
4  5  0.3112  0
6  7  0.0035  0
8  9  0.9812  0


짝수 홀수야 그냥 조심하면 되지만 n의 배수는 문제가 될 수 있습니다.


따라서 df.index에 1을 더하는 방식으로 이 문제를 우회할 필요가 있습니다.


3의 배수 행만 구하는 코드는 이렇게 됩니다.

print(df.assign(g=(df.index+1) % 3).query('g==0'))
   x       y  g
2  3  0.1322  0
5  6  0.7853  0
8  9  0.9812  0


세 가지를 모두 살펴 보신 분이라면 원리는 전부 같지만 프로그램마다 조금씩 디테일에서 차이가 난다는 사실을 확인하셨을 겁니다.


그럼 모두들 슬기로운 '데이터 노가다' 생활을 즐기시길 바라며 글을 마치겠습니다.


댓글,

Kidult/Excelsior | 카테고리 다른 글 더 보기