개발자

아퀘로 라이브러리로 자바스크립트 객체 필터링하기

2022.11.18
자바스크립트를 사용한 코딩에는 많은 이점이 있지만 데이터 랭글링은 아마 그 이점에 포함되지 않을 것이다. 그러나 자바스크립트 데이터 랭글링에 어려움을 느끼는 사람들을 위한 좋은 소식도 있다. 높은 인기를 누리는 dplyr R 패키지의 기반인 '데이터 문법' 개념을 아퀘로(Arquero) 라이브러리를 활용해 자바스크립트에서도 쓸 수 있다는 것이다.
 
워싱턴 대학 인터랙티브 데이터 연구소에서 만들어진 아퀘로는 옵저버블(Observable) 자바스크립트 사용자들 사이에서 가장 유명하지만 다른 방법으로도 사용할 수 있ek. 그 중 하나가 Node.js다.
 
이 기사에서는 아퀘로를 사용해 자바스크립트 객체를 필터링하는 방법을 알아본다. 마지막 부분에 몇 가지 팁도 소개한다.
 
ⓒ Getty Images Bank
 

1단계. 아퀘로 로드

아퀘로는 옵저버블 자바스크립트와 콰르토(Quarto)에서 표준 라이브러리고 필자도 이 방법으로 아퀘로를 사용한다. 설치는 불필요하다. 노드에서 아퀘로를 사용한다면 npm install arquero --save로 설치해야 한다. 브라우저에서는 <script onerror="removeImage($(this));" src="https://cdn.jsdelivr.net/npm/arquero@latest"></script>를 사용한다. 옵저버블의 경우 import {aq, op} from "@uwdata/arquero"로 아퀘로를 로드할 수 있다. 브라우저에서는 aq로 로드되고, 노드에서는 const aq = require('arquero')로 로드할 수 있다.
 
옵저버블과 콰르토의 나머지 코드는 소개한 그대로 실행해야 한다. 노드와 같은 비동기 환경에서 사용 중이라면 데이터 로드와 처리를 위해 필요한 부분을 조정해야 한다.
 

2단계. 데이터를 아퀘로 테이블로 변환

aq.from(my_object)를 사용해서 기존 “정규” 자바스크립트 객체를 아퀘로 테이블로 전환할 수 있다.
 
다른 방법은 아퀘로의 load 함수군을 사용해 원격 데이터를 직접 아퀘로 테이블로 가져오는 것이다. CSV 파일용으로는 aq.loadCSV("myurl.com/mycsvfile.csv") 함수, 웹의 JSON 파일용으로는 aq.loadJSON("myjsonurl.com/myjsonfile.json") 함수가 있다. 테이블 입력 함수에 대한 더 자세한 내용은 아퀘로 API 문서 웹사이트에서 볼 수 있다.
 
튜토리얼을 계속 진행하려면 아래 코드를 실행해서 미국 주의 인구 변화에 대한 샘플 데이터를 가져오자.
 
states_table = aq.loadCSV("https://raw.githubusercontent.com/smach/SampleData/master/states.csv")
 
아퀘로 테이블에는 옵저버블 자바스크립트와 콰르토에서 사용하기 위한 특수한  view() 메서드가 있다. states_table.view() 명령은 그림 1과 같은 출력을 반환한다.
 
아퀘로 테이블의 view() 메서드를 사용한 결과 ⓒ Sharon Machlis

옵저버블 자바스크립트의 Inputs.table(states_table)(정렬을 위한 클릭 가능한 열 헤더가 있음)도 아퀘로 테이블을 표시한다.
 
옵저버블 외부에서는 states_table.print()를 사용해서 콘솔로 테이블을 출력할 수 있다.
 

3단계. 행 필터

아퀘로 테이블에는 예를 들어 filter()를 사용한 특정 조건의 행 필터링 등 데이터 랭글링과 분석을 위한 많은 내장 메서드가 있다.
 
R 사용자 참고 사항: 아퀘로의 filter() 구문은 dplyr의 filter(Region == 'RegionName')만큼 단순하지는 않다. 이건 자바스크립트고 대부분의 함수는 벡터화되지 않으므로 d =>로 익명 함수를 만든 다음 그 안에서 다른 함수를 실행해야 한다(보통 op의 함수(위에서 arquero로 가져옴)). 자바스크립트 외의 언어에 익숙하다 해도 이 구조만 손에 익으면 쉽게 사용할 수 있을 것이다.
 
일반적인 구문은 다음과 같다.
 
filter(d => op.opfunction(d.columnname, 'argument') 
 
이 예제에서 필자가 원하는 op 함수는 op.equal()이다. 이 함수는 이름에서 짐작할 수 있듯이 동등성을 테스트한다. 따라서 미국 북동 지역의 주에 대한 Arquero 코드는 다음과 같을 것이다.
 
states_table
  .filter(d => op.equal(d.Region, 'Northeast'))
 
끝에 .view()를 붙여서 결과를 볼 수 있다.
 
filter() 구문에 대한 참고 사항: filter() 내의 코드는 아퀘로 테이블 식이다. 아퀘로 API 참조 웹사이트의 설명에 따르면 “얼핏 보면 테이블 식은 일반적인 자바스크립트 함수처럼 보이지만, 아퀘로는 내부에서 효과적으로 데이터를 관리하기 위해 함수 정의 모음을 취해서 이를 문자열에 매핑한 다음 파싱하고 다시 쓰고 컴파일한다.”
 
이 말이 무슨 의미일까? 일반적인 자바스크립트 함수 구문 외에 filter("d => op.equal(d.Region, 'Northeast')") 또는 filter("equal(d.Region, 'Northeast')")와 같은 특수한 테이블 식 구문도 사용할 수 있다는 의미다. 이러한 버전 중 하나가 더 매력적이거나 유용하다고 생각된다면 API 참조를 살펴보라.
 
또한 filter() 내부와 다른 아퀘로 동사(verb)에서 아무 유형의 자바스크립트 함수나 사용할 수 없다는 의미이기도 하다. 예를 들어 for 루프는 escape() 식 헬퍼로 래핑되지 않는 한 사용할 수 없다. 아퀘로 API 참조에서 관련 내용을 더 자세히 알아볼 수 있다.
 
파이썬 사용자 참고 사항 : 아퀘로 filterpandas.filter에서 볼 수 있듯이 행 또는 열이 아닌 행 부분집합 전용으로 설계됐다(열에 대해서는 잠시 후에 다룸.)
 
부정 또는 여러 조건을 사용하면 필터가 단일 테스트 이상으로 복잡해질 수 있다. 예를 들어 “서부 지역의 한 단어로 된 주 이름”을 원한다면 공백을 포함하지 않으며 지역이 서부인 주의 이름을 찾을 것이다. 유일한 방법은 filter(d =>) 익명 함수 내의 !op.includes(d.State, ' ') && op.equal(d.Region, 'West')이다.
 
states_table
  .filter(d => !op.includes(d.State, ' ') && 
     op.equal(d.Region, 'West'))
 
 
동등성 대신 정규식을 기준으로 검색하고 필터링하려면 op.equal() 대신 op.match()를 사용하면 된다.
 

4단계. 열 선택

특정 열 선택은 dplyr의 select()와 비슷하다. 사실 선택을 배열로 변환할 필요가 없으므로 더 쉽다. 인수는 select(): 내의 쉼표로 구분된 열 이름이 전부다.
 
states_table
  .select('State', 'State Code', 'Region', 'Division', 'Pop_2020')
 
 
select{{ OldName1: 'NewName1', OldName2: 'NewName2' }) 구문을 사용해서 열을 선택하면서 이름을 바꿀 수 있다. 예를 들면 다음과 같다.
 
states_table
  .select({ State: 'State', 'State Code': 'Abbr', Region: 'Region', 
      Division: 'Division', Pop_2020: 'Pop' })
 

5단계. 테이블 열 안에 고유한 값 배열 만들기

입력 드롭다운 채우기와 같은 작업에서는 한 열의 고유한 값을 기본 자바스크립트 배열로 받는 것이 유용할 수 있다. 아퀘로에는 이럴 때 쓰이는 함수가 여러 가지 있다.
 
  •  dedupe() – 고유한 값을 받는다.
  •  orderby() – 결과를 정렬한다.
  •  array() – 아퀘로 테이블 열의 데이터를 일반적인 자바스크립트 배열로 변환한다.
 
states_table에서 고유한 Division 이름의 정렬된 배열을 만드는 방법은 다음과 같다.
 
 region_array = states_table
  .select('Region')                                      
  .dedupe()
  .orderby('Region')
  .array('Region')

 이 새 객체는 자바스크립트 배열이므로 아퀘로 메서드는 더 이상 동작하지 않지만 일반적인 배열 메서드는 동작한다. 예를 들면 다음과 같다.
 
'The regions are ' + region_array.join(', ')

이 코드의 출력은 다음과 같다.
 
 "The regions are , Midwest, Northeast, South, West"
 
위 문자열의 첫 번째 쉼표는 배열에 널 값이 있기 때문에 존재한다. 널과 같은 빈 값을 삭제하고 싶다면 결과에 대해 아퀘로의 op.compact() 함수를 사용하면 된다.
 
region_array2 = op.compact(states_table
  .select('Region')                                      
  .dedupe()                                                                 
  .orderby('Region')
  .array('Region')
  ) 

다른 방법은 기본 자바스크립트 filter()를 사용해서 텍스트 문자열 배열에서 널 값을 제거하는 것이다. 참고로 다음 기본 자바스크립트 filter() 함수는 1차원 자바스크립트 배열을 위한 함수로, 2차원 아퀘로 테이블을 위한 아퀘로의 filter()와 다르다.
 
 region_array3 = states_table
  .select('Region')                                      
  .dedupe()                                                                 
  .orderby('Region')
  .array('Region')
  .filter(n => n)

콰르토 사용자를 포함한 옵저버블 자바스크립트 사용자는 md 함수를 사용해서 문자열에 스타일을 추가할 수도 있다(예를 들어 **를 사용한 굵은 글꼴). 다음 코드를 보자.
 
 md`The regions are **${region_array2.join(', ')}**.`

이 코드는 다음을 출력한다.
 
 The regions are Midwest, Northeast, South, West 

참고로 Intl.ListFormat() 자바스크립트 객체는 쉼표로 구분된 배열-문자열에서 마지막 항목 앞에 “and”를 손쉽게 추가할 수 있게 해준다. 다음 코드를 보자.
 
my_formatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
my_formatter.format(region_array3) 
 
이 코드는 다음과 같이 출력된다.
 
 "Midwest, Northeast, South, and West"
 

그 외의 다양한 아퀘로 기능

필터링, 선택, 중복 제거, 배열 만들기는 아퀘로가 할 수 있는 기능의 극히 일부분에 불과하다. 아퀘로 라이브러리에는 데이터 재구조화, 병합, 집계 등을 위한 동사와 평균, 중앙값, 변위치, 순위, 후발, 선도와 같은 계산 및 분석을 위한 op 함수가 있다. 더 많은 기능은 아퀘로 소개 문서에서 볼 수 있다. 또한 아퀘로 동사에 대한 도해 가이드도 있고, 아퀘로 API 문서에서 전체 목록을 볼 수 있으며 데이터 랭글러 옵저버블 노트북에는 아퀘로로 할 수 있는 일을 보여주는 인터랙티브 애플리케이션이 있다.
editor@itworld.co.kr 

회사명 : 한국IDG | 제호: ITWorld | 주소 : 서울시 중구 세종대로 23, 4층 우)04512
| 등록번호 : 서울 아00743 등록발행일자 : 2009년 01월 19일

발행인 : 박형미 | 편집인 : 박재곤 | 청소년보호책임자 : 한정규
| 사업자 등록번호 : 214-87-22467 Tel : 02-558-6950

Copyright © 2024 International Data Group. All rights reserved.