생산성 향상을 위한 나만의 R 패키지 빌드

Loading...

생산성 향상을 위한 나만의 R 패키지 빌드

Loading...

R 패키지 현황

R 패키지는 R 프로그래밍 언어의 기능을 확장하는 다양한 도구와 함수 모음을 의미합니다. 현재 R 생태계에서 수많은 패키지가 다양한 분야에서 사용되고 있으며, 이들은 R 사용자의 연구와 분석 작업을 간소화하고 개선하는 데 중요한 역할을 하고 있습니다. R 패키지의 현황에 대해 주요한 측면을 아래에 설명드리겠습니다.

1. 패키지의 수와 다양성

  • CRAN (The Comprehensive R Archive Network): R 패키지의 대부분은 CRAN에서 관리됩니다. 2024년 기준으로 CRAN에는 19,000개 이상의 패키지가 등록되어 있으며, 이는 통계학, 데이터 과학, 생물정보학, 경제학 등 다양한 분야를 포괄합니다. (예시: kisopenapi)
  • Bioconductor: 생명과학과 생물정보학 분야에 특화된 R 패키지들이 주로 포함되어 있으며, 게놈 분석, 시퀀싱 데이터 처리 등 생명과학 연구에 필수적인 도구들이 다수 존재합니다. (예시: qmtools)
  • GitHub: 많은 개발자들이 CRAN에 등록하지 않은 최신 버전의 패키지나 실험적인 패키지를 GitHub에 배포하고 있습니다. 이는 R 커뮤니티가 빠르게 변화하는 연구 요구에 대응할 수 있게 합니다.

2. 인기 패키지

  • 데이터 처리: dplyr, data.table 등은 데이터 조작과 처리에 강력한 기능을 제공하며, 데이터 분석의 필수적인 도구로 자리 잡았습니다.
  • 데이터 시각화: ggplot2는 유연하고 강력한 그래픽 시스템을 제공하며, R 사용자가 데이터 시각화를 할 때 가장 많이 사용하는 패키지 중 하나입니다.
  • 기계 학습: caret, xgboost, randomForest와 같은 패키지는 R에서 기계 학습 모델을 구축하고 평가하는 데 자주 사용됩니다.
  • 통계 분석: MASS, lme4, survival과 같은 패키지는 고급 통계 분석을 위한 기능을 제공합니다.

3. 패키지 관리 및 설치

  • R 패키지는 일반적으로 install.packages() 함수를 사용해 설치하며, CRAN, Bioconductor, GitHub 등 다양한 출처에서 패키지를 설치할 수 있습니다.
  • 패키지 버전 관리를 위해 renv와 같은 도구가 사용되며, 이는 프로젝트별로 패키지 버전을 고정하여 재현 가능한 분석 환경을 보장합니다.

4. 지속적인 발전

  • R 패키지는 커뮤니티의 기여를 통해 지속적으로 발전하고 있습니다. 많은 연구자와 개발자들이 최신 연구 결과와 분석 방법을 R 패키지 형태로 공유하며, 이는 R 생태계의 성장을 촉진합니다.
  • 패키지들은 지속적으로 업데이트되며, 최신 R 버전과 호환되도록 유지됩니다. 이를 통해 사용자들은 항상 최신 기술과 방법론을 사용할 수 있습니다.

5. 도전 과제

  • 호환성 문제: 패키지 간의 의존성 충돌이나 R의 새로운 버전과의 호환성 문제가 가끔 발생합니다. 이러한 문제를 해결하기 위해 renvpackrat과 같은 패키지 관리 도구들이 사용됩니다.
  • 사용 편의성: 패키지가 많아질수록 어떤 패키지를 사용해야 할지 결정하는 것이 어려워질 수 있습니다. 이를 해결하기 위해 분야별 추천 패키지 리스트나 튜토리얼이 제공되기도 합니다.

6. 미래 전망

  • R 패키지는 계속해서 증가할 것으로 예상되며, 특히 인공지능, 딥러닝, 빅데이터 분석 등의 분야에서 새로운 패키지가 많이 개발될 것입니다.
  • R과 Python 간의 인터페이스를 개선하는 패키지들이 늘어남에 따라, 두 언어의 장점을 모두 활용할 수 있는 혼합 분석 환경이 더욱 발전할 것입니다.

결론적으로, R 패키지는 R의 기능을 확장하고 연구와 분석 작업을 효율적으로 수행하는 데 핵심적인 역할을 하고 있으며, R 커뮤니티의 적극적인 참여와 기여로 인해 계속해서 발전하고 있습니다.

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발은 왜 개인적 생산성 향상에 중요한가

R 패키지 개발은 단순히 공유를 목적으로 한 작업뿐만 아니라, 개인적인 생산성과 효율성을 높이기 위한 중요한 도구로서도 매우 유용합니다. 패키지 개발의 과정을 통해 얻을 수 있는 이점들을 살펴보면, 개인적인 프로젝트나 업무에서 R 패키지를 개발하는 것이 왜 가치 있는지 이해할 수 있습니다.

1. 코드 재사용성 및 관리 용이성

개발자들은 종종 유사한 코드나 기능을 여러 프로젝트에서 반복적으로 사용해야 하는 상황에 직면합니다. 이러한 코드를 R 패키지로 묶어두면, 다음과 같은 이점이 있습니다:

  • 코드 재사용: 동일한 기능을 여러 프로젝트에서 사용할 수 있도록 모듈화된 함수를 패키지로 묶어두면, 매번 동일한 코드를 다시 작성할 필요가 없습니다. 이는 시간과 노력을 절약할 수 있게 해줍니다.
  • 중앙 집중식 관리: 모든 관련 함수와 데이터가 한 패키지에 모여 있기 때문에, 업데이트나 유지 관리가 용이합니다. 코드의 버그를 수정하거나 성능을 개선할 때, 패키지를 업데이트하면 이를 사용하는 모든 프로젝트에 자동으로 반영됩니다.
  • 일관성 유지: 일관된 함수 인터페이스와 로직을 패키지로 정의하면, 다양한 프로젝트에서 사용되는 코드의 일관성을 유지할 수 있습니다. 이는 코드의 신뢰성을 높이는 데 기여합니다.

2. 문서화 및 유지보수성

R 패키지 개발 과정에서는 함수와 데이터에 대한 문서화를 체계적으로 관리할 수 있습니다. 이를 통해 다음과 같은 이점이 있습니다:

  • 자동 문서화: roxygen2 같은 도구를 사용하면, 함수의 주석을 바탕으로 자동으로 문서를 생성할 수 있습니다. 이렇게 생성된 문서는 나중에 함수 사용법을 쉽게 참조할 수 있게 해주며, 자신이 작성한 코드라도 시간이 지난 후에 이해하기 쉬워집니다.
  • 유지보수성 향상: 잘 문서화된 패키지는 시간이 지나도 쉽게 유지보수할 수 있습니다. 패키지를 사용하여 작성된 코드가 명확하게 이해되므로, 기능 추가나 수정 작업이 간편해집니다.
  • 지식의 저장: 패키지는 개발자가 학습하고 발견한 지식을 저장하는 훌륭한 도구입니다. 이를 통해 나중에 비슷한 문제를 다시 마주쳤을 때 빠르게 해결할 수 있습니다.

3. 작업 효율성 및 생산성 향상

패키지를 사용하면 작업을 보다 체계적으로 관리할 수 있으며, 반복 작업을 줄이고 복잡한 분석을 쉽게 수행할 수 있습니다.

  • 자동화된 작업 흐름: R 패키지를 통해 반복적인 분석 작업이나 데이터 처리 작업을 자동화할 수 있습니다. 이는 작업 시간을 단축시키고, 수동으로 작업하면서 발생할 수 있는 실수를 줄여줍니다.
  • 모듈화된 코드 구조: 코드가 모듈화되고 패키지 형태로 구조화되면, 프로젝트의 복잡성이 줄어들고, 각 모듈을 독립적으로 테스트하고 개선할 수 있습니다. 이는 코드의 안정성과 확장성을 높이는 데 기여합니다.
  • 생산성 향상: 일상적인 데이터 분석 작업에서 사용되는 도구와 기능을 패키지로 만들어 두면, 분석 작업을 빠르게 시작할 수 있고, 새로운 분석 방법을 쉽게 도입할 수 있습니다.

4. 전문성 향상 및 학습 기회

R 패키지 개발은 개인의 프로그래밍 기술과 데이터 분석 능력을 향상시키는 데 큰 도움이 됩니다.

  • 코딩 스킬 향상: 패키지 개발 과정에서 모듈화, 코드 구조화, 문서화, 테스트 등의 다양한 개발 기술을 학습하게 됩니다. 이는 일반적인 스크립트 작성과는 다른 깊이의 기술을 요구하며, 개발자의 전반적인 프로그래밍 실력을 높여줍니다.
  • 지속적인 학습: 패키지를 유지보수하고 개선해 나가는 과정에서 새로운 R 기능이나 패키지, 개발 방법론을 배울 수 있는 기회가 됩니다. 이는 개발자로서 지속적으로 성장할 수 있는 발판이 됩니다.
  • 전문적인 프로젝트 관리: 패키지 개발은 개인 프로젝트를 보다 전문적이고 체계적으로 관리하는 방법을 배우는 과정이기도 합니다. 이는 개인적인 프로젝트뿐만 아니라, 팀 단위의 협업 프로젝트에서도 큰 도움이 됩니다.

5. 장기적인 투자

개인적인 목적으로 패키지를 개발하는 것은 장기적인 관점에서 매우 가치 있는 투자입니다.

  • 확장 가능성: 처음에는 개인적인 용도로 시작한 패키지가 나중에 공유하거나 배포할 만큼 발전할 수 있습니다. 이는 개인적인 생산성 향상뿐만 아니라, 커뮤니티에 기여할 기회도 제공합니다.
  • 지속적인 개선: 패키지는 시간이 지남에 따라 계속해서 개선하고 확장할 수 있습니다. 이를 통해 개인적인 도구가 점점 더 강력해지고 유용해질 수 있습니다.
  • 경력 개발: 패키지 개발 경험은 프로필을 더욱 풍부하게 만들며, 데이터 분석가나 R 개발자로서의 경력을 쌓는 데도 큰 도움이 됩니다.

결론

R 패키지 개발은 개인적인 생산성을 높이는 데 있어서 매우 유용한 도구입니다. 이를 통해 코드 재사용성, 문서화, 작업 효율성, 전문성 향상 등 다양한 측면에서 이점을 누릴 수 있습니다. 따라서, 비록 패키지를 공개할 계획이 없더라도, 개인적인 프로젝트의 질을 높이고 장기적인 성장을 도모하기 위해 R 패키지 개발을 고려하는 것은 매우 가치 있는 선택입니다.

생산성 향상을 위한 나만의 R 패키지 빌드

개발 환경 (24.09)

1. 운영체제

  • OS: Ubuntu 22.04 LTS (개발), macOS 15.0 (화면캡쳐)

2. 프로그래밍 언어

  • R: 4.3.3
  • C/C++: GCC 11.4.0
  • Python: 3.9.18

3. 편집기

  • Vim: 8.2
  • Visual Studio Code: 1.91.1

4. 프레임워크

  • RStudio Server: 2024.04.2
  • Shiny Server: v1.5.22.1017
  • NGINX: 1.18.0
  • FFmpeg: 7.0.2 (GIF 파일 생성)

개발 패키지 (예시)

1. CRAN 배포

  • ecos: 한국은행
  • kisopenapi: 한국투자증권
  • kosis: 국가통계포털

2. 미배포

  • ggshort: ggplot2
  • jaid: C
  • papagor: Python
  • shinymodules: Shiny Modules
  • navergmail: blastula

생산성 향상을 위한 나만의 R 패키지 빌드 (Please wait...)

Loading...

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발에서 DESCRIPTION 파일의 구조와 역할

DESCRIPTION 파일은 R 패키지 개발의 중요한 구성 요소 중 하나로, 패키지의 메타데이터를 정의하는 역할을 합니다. 이 파일은 패키지의 기본 정보, 의존성, 저자 정보 등을 포함하고 있으며, R 패키지를 빌드하고 설치할 때 중요한 역할을 합니다. 다음은 DESCRIPTION 파일의 구조와 각 필드의 역할에 대한 자세한 설명입니다.

1. Package

  • 역할: 패키지의 이름을 정의합니다. R 패키지 이름은 고유해야 하며, 일반적으로 대소문자를 구분하지 않습니다.
  • 예시: Package: mypackage

2. Version

  • 역할: 패키지의 버전을 나타냅니다. 버전은 일반적으로 major.minor.patch 형식으로 작성되며, 패키지의 업데이트와 변화에 따라 버전을 갱신합니다.
  • 예시: Version: 1.0.0

3. Title

  • 역할: 패키지의 짧은 설명을 제공합니다. 이는 패키지의 목적을 간단하게 설명하는 한 줄짜리 문구입니다.
  • 예시: Title: A Package for Data Analysis

4. Description

  • 역할: 패키지의 기능과 목적에 대한 좀 더 자세한 설명을 제공합니다. 이 필드는 R 사용자들에게 패키지가 어떤 문제를 해결하는지, 또는 어떤 기능을 제공하는지에 대해 명확히 전달합니다.
  • 예시:
    Description: This package provides tools for data analysis, including 
                 functions for data manipulation, visualization, and statistical modeling.
    

5. AuthorMaintainer

  • 역할: 패키지의 저자 및 유지 관리자를 지정합니다. 저자는 패키지를 개발한 사람 또는 조직이고, 유지 관리자는 패키지의 현재 상태를 관리하고 업데이트하는 역할을 합니다.

  • 예시:

    Author: John Doe [aut, cre], Jane Smith [ctb]
    Maintainer: John Doe <johndoe@example.com>
    
    • [aut]: Author (저자)
    • [cre]: Creator (유지 관리자)
    • [ctb]: Contributor (기여자)

6. License

  • 역할: 패키지의 라이선스를 명시합니다. 이는 사용자가 패키지를 사용할 때 따라야 할 규칙을 정의합니다. 일반적으로 GPL, MIT, Apache License 등이 사용됩니다.
  • 예시: License: GPL-3

7. Depends, Imports, Suggests, Enhances

  • 역할: 패키지가 필요로 하는 다른 패키지들의 의존성을 정의합니다.

    • Depends: 패키지의 기본 기능을 사용하기 위해 반드시 필요한 패키지들.
    • Imports: 패키지 내부적으로 사용하지만 사용자 인터페이스에 직접 노출되지 않는 패키지들.
    • Suggests: 패키지가 선택적으로 사용하는 패키지들. 기능이 강화되지만 필수적이지 않음.
    • Enhances: 패키지가 다른 패키지를 강화하는 경우에 사용.
  • 예시:

    Depends: R (>= 3.5.0)
    Imports: dplyr, ggplot2
    Suggests: testthat, knitr
    

8. LazyData

  • 역할: 패키지에 포함된 데이터가 lazy-loading 방식으로 로드될지를 정의합니다. TRUE로 설정하면 데이터가 패키지 로드 시점이 아닌, 필요할 때 로드됩니다.
  • 예시: LazyData: true

9. Encoding

  • 역할: DESCRIPTION 파일의 인코딩 방식을 지정합니다. 일반적으로 UTF-8이 많이 사용됩니다.
  • 예시: Encoding: UTF-8

10. RoxygenNote

  • 역할: roxygen2 패키지를 사용하여 문서를 생성할 때, 해당 버전을 기록하는 필드입니다. 이는 roxygen2로 생성된 문서와 일관성을 유지하는 데 도움이 됩니다.
  • 예시: RoxygenNote: 7.1.1

11. URL, BugReports

  • 역할: 패키지의 웹사이트, GitHub 저장소 링크, 또는 버그 리포트용 URL을 제공하여 사용자들이 패키지와 상호작용할 수 있도록 합니다.
  • 예시:
    URL: https://github.com/username/mypackage
    BugReports: https://github.com/username/mypackage/issues
    

12. 기타 필드

  • VignetteBuilder: 패키지에 포함된 비네트(vignette)를 작성하는 데 필요한 패키지 지정.
  • Depends, Suggests 외에도 LinkingTo(C/C++ 라이브러리 의존성) 등 다양한 필드가 존재하며, 패키지의 복잡성에 따라 추가될 수 있습니다.

DESCRIPTION 파일은 R 패키지를 정의하는 필수적인 부분으로, 이 파일을 통해 패키지의 기본적인 정보를 R과 사용자에게 전달할 수 있습니다. 이 파일을 올바르게 작성하는 것은 패키지 개발에서 매우 중요합니다.

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발에서 NAMESPACE 파일의 구조와 역할

NAMESPACE 파일은 R 패키지 개발에서 중요한 역할을 하며, 패키지의 함수와 데이터 객체의 노출 및 사용을 관리합니다. 이 파일은 패키지 사용자와 R 자체에게 패키지의 어느 부분이 외부에 노출되고, 패키지 내부에서 어떤 다른 패키지를 사용할 것인지 등을 명시합니다.

NAMESPACE 파일의 주요 역할

  • 패키지 내부의 함수 및 객체의 외부 노출을 제어: 패키지 사용자가 직접 사용할 수 있는 함수와 그렇지 않은 함수를 정의합니다.
  • 다른 패키지의 함수 및 객체의 사용 제어: 패키지 내에서 다른 패키지의 특정 함수나 객체를 임포트할 수 있습니다.

NAMESPACE 파일의 주요 지시문 (directives)

NAMESPACE 파일은 여러 가지 지시문(directive)을 포함할 수 있으며, 각 지시문은 특정 역할을 수행합니다.

1. export

  • 역할: 패키지의 특정 함수나 객체를 외부에 노출합니다. 이 지시문을 통해 지정된 함수는 패키지를 사용하는 사람들이 직접 접근할 수 있습니다.
  • 예시: export(my_function)
    • my_function이라는 함수가 패키지를 사용하는 사람들에게 노출됨을 의미합니다.

2. exportPattern

  • 역할: 정규 표현식을 사용하여 일치하는 모든 함수나 객체를 외부에 노출합니다. 주로 특정 접두사나 접미사를 가진 함수들을 한꺼번에 노출할 때 사용됩니다.
  • 예시: exportPattern("^[a-z]+_plot$")
    • 모든 소문자로 시작하고 _plot으로 끝나는 함수들을 외부에 노출합니다.

3. import

  • 역할: 다른 패키지의 모든 함수와 객체를 가져와 패키지 내에서 사용합니다. import 지시문을 사용하면 해당 패키지의 모든 공개 함수에 접근할 수 있습니다.
  • 예시: import(ggplot2)
    • ggplot2 패키지의 모든 함수를 가져와 사용 가능하게 합니다.

4. importFrom

  • 역할: 특정 패키지에서 특정 함수나 객체만을 선택적으로 가져옵니다. 패키지의 특정 기능만 필요할 때 효율적으로 사용할 수 있습니다.
  • 예시: importFrom(dplyr, select, filter)
    • dplyr 패키지에서 selectfilter 함수만 가져옵니다.

5. importClassesFrom

  • 역할: 다른 패키지에서 특정 S4 클래스 정의를 가져옵니다.
  • 예시: importClassesFrom(methods, "data.frame")
    • methods 패키지에서 data.frame 클래스를 가져옵니다.

6. importMethodsFrom

  • 역할: 다른 패키지에서 특정 S4 메서드를 가져옵니다.
  • 예시: importMethodsFrom(methods, "show")
    • methods 패키지에서 show 메서드를 가져옵니다.

7. exportClasses

  • 역할: 패키지 내에서 정의된 S4 클래스를 외부에 노출합니다.
  • 예시: exportClasses(MyClass)
    • MyClass라는 S4 클래스를 외부에 노출합니다.

8. exportMethods

  • 역할: 패키지 내에서 정의된 S4 메서드를 외부에 노출합니다.
  • 예시: exportMethods(plot)
    • plot 메서드를 외부에 노출합니다.

9. S3method

  • 역할: S3 메서드를 지정하여 해당 메서드가 특정 클래스의 객체에 대해 호출될 때 사용됩니다.
  • 예시: S3method(print, myClass)
    • myClass 객체에 대해 print 메서드를 사용하도록 지정합니다.

NAMESPACE 파일의 예시

아래는 NAMESPACE 파일의 간단한 예시입니다.

export(my_function)
exportPattern("^[a-z]+_plot$")

import(ggplot2)
importFrom(dplyr, select, filter)

importClassesFrom(methods, "data.frame")
importMethodsFrom(methods, "show")

exportClasses(MyClass)
exportMethods(plot)

S3method(print, myClass)

NAMESPACE 파일의 중요성

  • 패키지의 기능 캡슐화: NAMESPACE 파일을 통해 패키지 개발자는 패키지 내부 구조를 사용자로부터 감추고, 필요한 부분만을 노출함으로써 패키지의 일관성과 안정성을 유지할 수 있습니다.
  • 의존성 관리: 패키지 간의 의존성을 명확하게 정의하여, 어떤 패키지의 어떤 부분이 사용되는지를 명확히 할 수 있습니다.
  • 명확한 인터페이스 제공: 사용자에게 명확하고 일관된 인터페이스를 제공함으로써 패키지의 사용 편의성을 높일 수 있습니다.

NAMESPACE 파일은 R 패키지 개발에서 매우 중요한 역할을 하며, 패키지의 기능을 효과적으로 관리하고 사용자에게 제공하기 위해 반드시 올바르게 작성되어야 합니다.

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발에서 R 폴더의 구조와 역할

R 패키지 개발에서 R 폴더는 패키지의 핵심 기능을 구현하는 곳으로, R 코드가 저장되는 주요 디렉토리입니다. 이 폴더에 포함된 파일들은 패키지 내의 함수, 클래스, 메서드 등을 정의하며, 패키지 사용자가 접근하게 될 대부분의 기능들이 이곳에 구현됩니다.

R 폴더의 구조와 역할

1. 파일 구조

  • R 폴더에는 주로 .R 확장자를 가진 파일들이 들어갑니다.
  • 각 파일은 하나 이상의 함수를 정의할 수 있으며, 패키지의 논리적 구성에 따라 여러 파일로 나눌 수 있습니다.
  • 파일 이름은 기능별로 명확하게 지정하는 것이 좋습니다. 예를 들어, 데이터 처리와 관련된 함수들은 data_processing.R, 시각화 함수들은 plotting.R 등으로 구분할 수 있습니다.

2. 역할

  • 함수 정의: 패키지의 주요 함수들이 이곳에 정의됩니다. 이 함수들은 패키지 사용자가 호출할 수 있으며, NAMESPACE 파일에 의해 외부에 노출됩니다.
  • 내부 함수 관리: 패키지의 내부 동작에 필요한 함수들도 이곳에 정의됩니다. 이러한 함수들은 패키지 사용자에게 직접 노출되지 않을 수 있으며, 주로 패키지의 내부 논리 구현에 사용됩니다.
  • 클래스 및 메서드 정의: S3 또는 S4 클래스와 메서드도 이 폴더에서 정의됩니다. 이 클래스들은 패키지의 복잡한 데이터 구조를 관리하는 데 사용됩니다.
  • 코드 조직화: 패키지의 다양한 기능을 조직화하고 관리할 수 있도록, 관련된 함수들을 하나의 파일에 그룹화하여 유지 관리가 쉽도록 합니다.

3. 파일 작성 및 관리

  • 주석과 문서화: 각 함수는 적절한 주석을 포함하여, 코드의 가독성을 높이고 유지 보수성을 강화해야 합니다. 또한, roxygen2와 같은 도구를 사용해 함수의 문서화를 진행할 수 있습니다.
  • 코드 스타일: 일관된 코드 스타일을 유지하는 것이 중요합니다. 이는 코드의 가독성을 높이고, 여러 개발자가 협업할 때 효율성을 증대시킵니다.
  • 테스트 포함: 패키지의 기능을 검증하기 위해 R 코드 내에 간단한 테스트를 포함할 수 있습니다. 그러나 대규모 테스트는 보통 tests 폴더에 별도로 관리합니다.

4. 함수 파일의 예시

  • 데이터 처리 함수 정의 (data_processing.R):
    # 데이터 정리 함수
    clean_data <- function(data) {
      # 결측값 제거
      data <- na.omit(data)
      return(data)
    }
    
    # 데이터 변환 함수
    transform_data <- function(data, transformation) {
      if (transformation == "log") {
        return(log(data))
      } else if (transformation == "sqrt") {
        return(sqrt(data))
      } else {
        stop("지원되지 않는 변환입니다.")
      }
    }
    
  • 시각화 함수 정의 (plotting.R):
    # 간단한 시각화 함수
    plot_data <- function(data) {
      plot(data, main = "데이터 시각화", xlab = "X 축", ylab = "Y 축")
    }
    

5. R 폴더와 다른 디렉토리의 상호작용

  • NAMESPACE 파일과의 연계: R 폴더에 정의된 함수들은 NAMESPACE 파일에 의해 패키지 사용자에게 노출되거나 내부에서만 사용되도록 관리됩니다.
  • 테스트 폴더(tests)와의 연계: R 폴더에서 정의된 함수들은 tests 폴더에 작성된 테스트 코드에서 검증됩니다. 이는 패키지의 안정성을 높이는 데 중요한 역할을 합니다.
  • 데이터 폴더(data, inst 등)와의 연계: 함수들이 데이터 폴더에 저장된 데이터셋을 처리하거나 활용하는 경우, 이러한 폴더들과 밀접하게 상호작용합니다.

결론

R 폴더는 R 패키지 개발에서 매우 중요한 부분으로, 패키지의 기능적 핵심을 담고 있습니다. 이 폴더를 잘 조직하고 관리하는 것은 패키지의 유지 보수성과 확장성을 높이는 데 필수적입니다. 이 폴더 내의 코드들은 패키지의 전반적인 성능과 품질을 결정짓는 중요한 요소입니다.

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발에서 man 폴더의 구조와 역할

man 폴더는 R 패키지 개발에서 매우 중요한 역할을 담당하는 디렉토리로, 패키지의 함수, 데이터셋, 클래스, 메서드 등에 대한 문서 파일들이 저장되는 곳입니다. 이 폴더의 문서 파일들은 패키지 사용자가 각 객체의 사용법을 쉽게 이해할 수 있도록 돕는 역할을 하며, R의 도움말 시스템과 직접 연동됩니다.

man 폴더의 구조와 역할

1. 파일 구조

  • man 폴더는 .Rd 확장자를 가진 여러 문서 파일들로 구성됩니다. .Rd는 “R documentation”을 의미하며, 이는 R에서 사용되는 특수한 마크업 언어로 작성된 문서 파일입니다.
  • man 폴더 내의 각 파일은 패키지 내 특정 함수, 데이터셋, 또는 클래스에 대한 문서입니다. 예를 들어, my_function이라는 함수의 문서는 my_function.Rd라는 파일명으로 저장됩니다.
  • 이 문서 파일들은 R의 도움말 시스템에서 사용자가 ?my_function 또는 help(my_function) 명령어를 통해 접근할 수 있습니다.

2. 역할

  • 함수 및 객체의 문서화: man 폴더 내의 각 .Rd 파일은 패키지 사용자가 함수나 데이터셋의 사용법을 이해하는 데 도움을 줍니다. 이 문서들은 함수의 인자, 반환값, 예제 코드 등을 포함하여 사용자가 패키지의 기능을 쉽게 활용할 수 있도록 합니다.
  • 도움말 시스템과 연동: R 패키지에서 man 폴더는 도움말 시스템과 밀접하게 연관되어 있습니다. 사용자가 특정 함수나 데이터셋의 도움말을 요청할 때, man 폴더 내의 해당 .Rd 파일이 로드되어 도움말 내용이 표시됩니다.
  • 패키지 문서의 일관성 유지: 모든 함수와 객체에 대한 일관된 문서화를 통해, 패키지 사용자들이 혼란 없이 패키지의 기능을 탐색하고 사용할 수 있도록 도와줍니다.

3. .Rd 파일의 주요 구성 요소

.Rd 파일은 여러 섹션으로 나뉘어 있으며, 각 섹션은 해당 함수나 객체의 특정 측면을 설명합니다. 일반적인 .Rd 파일의 구성 요소는 다음과 같습니다:

  • \name: 문서화된 객체의 이름을 지정합니다.

    • 예: \name{my_function}
  • \alias: 문서화된 객체의 다른 이름 또는 별칭(alias)을 지정합니다.

    • 예: \alias{my_function}
  • \title: 함수 또는 객체의 짧은 제목을 지정합니다. 이는 문서의 처음 부분에 표시됩니다.

    • 예: \title{My Custom Function}
  • \description: 함수 또는 객체의 목적과 기능을 간략히 설명합니다.

    • 예: \description{This function performs a custom operation on numeric data.}
  • \usage: 함수의 호출 방법을 설명합니다. 이는 함수의 시그니처를 보여줍니다.

    • 예: \usage{my_function(x, y)}
  • \arguments: 함수의 인자들에 대한 설명을 포함합니다. 각 인자에 대해 이름과 설명을 제공해야 합니다.

    • 예:
      \arguments{
        \item{x}{A numeric vector.}
        \item{y}{Another numeric vector.}
      }
      
  • \value: 함수가 반환하는 값에 대한 설명입니다.

    • 예: \value{A numeric value representing the sum of \code{x} and \code{y}.}
  • \examples: 함수 사용법에 대한 예제를 제공합니다. 이 예제 코드는 사용자가 함수의 실제 사용 방법을 이해하는 데 도움이 됩니다.

    • 예:
      \examples{
        result <- my_function(1:5, 6:10)
        print(result)
      }
      
  • \seealso: 관련 함수나 문서에 대한 참조 링크를 제공합니다.

    • 예: \seealso{\link{another_function}}
  • \details: 함수의 동작 원리나 특정 세부 사항을 자세히 설명합니다.

  • \author: 함수나 데이터셋의 저자를 명시합니다.

    • 예: \author{John Doe}
  • \keyword: 이 문서와 관련된 키워드를 정의하여, 사용자가 키워드 검색을 통해 쉽게 찾을 수 있도록 합니다.

    • 예: \keyword{utilities}

4. 문서 생성과 관리

  • roxygen2 패키지: 대부분의 R 패키지 개발자들은 roxygen2 패키지를 사용하여 함수 정의와 함께 주석을 달아 문서를 자동 생성합니다. 이 도구는 소스 코드에 있는 주석을 기반으로 자동으로 .Rd 파일을 생성하므로, 코드와 문서의 일관성을 유지하는 데 도움이 됩니다.

    • 예를 들어, R 코드 파일에서 다음과 같이 주석을 작성하면:
      #' My Custom Function
      #'
      #' This function performs a custom operation on numeric data.
      #'
      #' @param x A numeric vector.
      #' @param y Another numeric vector.
      #' @return A numeric value representing the sum of \code{x} and \code{y}.
      #' @examples
      #' result <- my_function(1:5, 6:10)
      #' print(result)
      my_function <- function(x, y) {
        return(x + y)
      }
      
      roxygen2 패키지가 이를 기반으로 .Rd 파일을 생성합니다.
  • 패키지 검사 (R CMD check): 패키지를 빌드하거나 배포하기 전에, R CMD check 도구를 사용해 문서화가 제대로 되었는지 확인할 수 있습니다. 이 도구는 모든 함수와 객체에 대해 문서화가 누락되지 않았는지, 문서가 올바르게 작성되었는지 검사합니다.

5. man 폴더의 중요성

  • 사용자 경험: 잘 작성된 문서는 사용자 경험을 크게 향상시킵니다. 사용자는 문서를 통해 패키지의 기능을 쉽게 이해하고 사용할 수 있으며, 이는 패키지의 성공 여부에 중요한 영향을 미칩니다.
  • 패키지 유지 보수: 문서화는 패키지의 유지 보수를 용이하게 합니다. 개발자가 시간이 지나도 패키지의 기능과 사용법을 쉽게 이해할 수 있도록 돕습니다.
  • 공식 배포 요건: CRAN과 같은 R 패키지 배포 플랫폼은 패키지의 모든 함수와 객체에 대해 적절한 문서화를 요구합니다. 따라서 man 폴더 내 문서가 적절히 작성되어 있어야 합니다.

결론

man 폴더는 R 패키지에서 사용자 문서화의 중심적인 역할을 하며, 패키지의 모든 기능에 대한 명확하고 포괄적인 설명을 제공하는 중요한 자료입니다. 이 폴더에 포함된 문서들은 사용자가 패키지의 다양한 기능을 최대한 활용할 수 있도록 돕는 중요한 역할을 합니다. 잘 관리된 man 폴더는 패키지의 사용성과 유지 보수성을 크게 향상시킵니다.

생산성 향상을 위한 나만의 R 패키지 빌드

usethis 패키지

usethis 패키지는 R 패키지 개발을 더 효율적이고 쉽게 만들기 위해 설계된 유틸리티 패키지입니다. 특히 R 패키지의 초기 설정, 문서화, GitHub와의 연동 등 여러 작업을 자동화하는 데 매우 유용합니다. usethis 패키지를 사용하면 복잡한 설정 과정을 줄이고 패키지 개발에 집중할 수 있습니다.

주요 기능 및 사용법

1. R 패키지 기본 구조 설정

패키지를 개발하려면 먼저 패키지의 기본 구조를 만들어야 합니다. usethis 패키지는 이 과정을 매우 간단하게 처리해 줍니다.

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

# 패키지 초기화
usethis::create_package("path/to/your/package")
  • create_package(): 주어진 경로에 새로운 R 패키지를 생성합니다. 기본적으로 DESCRIPTION, NAMESPACE, R/ 디렉토리 등의 필수 구조가 자동으로 생성됩니다.

2. Git 및 GitHub 연동

패키지를 개발할 때 버전 관리를 위해 Git과 GitHub를 사용하는 것이 일반적입니다. usethis는 Git을 쉽게 초기화하고 GitHub와 연동할 수 있는 도구를 제공합니다.

# Git 초기화
usethis::use_git()

# GitHub 연동
usethis::use_github()
  • use_git(): 패키지 디렉토리에서 Git을 초기화합니다.
  • use_github(): GitHub 저장소를 생성하고, 로컬 Git 저장소를 GitHub와 연결합니다.

3. R 스크립트와 함수 추가

패키지에 새로운 함수나 스크립트를 추가할 때, 올바른 파일 구조에 맞게 쉽게 파일을 생성할 수 있습니다.

# R/ 디렉토리에 새로운 R 파일 생성
usethis::use_r("my_function")
  • use_r(): R/ 디렉토리 내에 새로운 .R 파일을 생성합니다. 함수나 코드를 관리하기 좋게 새로운 파일을 만들어 줍니다.

4. 설명 파일 (DESCRIPTION) 자동 생성 및 수정

DESCRIPTION 파일은 패키지의 메타데이터를 포함하며, usethis는 이 파일을 쉽게 편집할 수 있도록 도와줍니다.

# DESCRIPTION 파일에 패키지 정보를 추가
usethis::use_description(fields = list(
  Title = "My Awesome Package",
  Description = "This package does amazing things."
))

5. 테스트 설정

패키지를 개발할 때 테스트 코드를 작성하는 것은 매우 중요합니다. usethistestthat 패키지를 사용하여 테스트 구조를 쉽게 설정할 수 있습니다.

# testthat을 이용한 테스트 환경 설정
usethis::use_testthat()

# 테스트 파일 생성
usethis::use_test("my_function")
  • use_testthat(): testthat 패키지를 사용하여 tests/ 디렉토리를 생성하고 테스트 환경을 설정합니다.
  • use_test(): 주어진 이름으로 테스트 파일을 생성합니다.

6. 문서화 설정

R 패키지에서 함수에 대한 문서화를 관리하는 것은 매우 중요합니다. usethis는 문서화 도구인 roxygen2와 함께 작동하여 문서화를 쉽게 할 수 있도록 돕습니다.

# roxygen2 문서화 환경 설정
usethis::use_roxygen_md()

# 패키지 문서화 파일 생성
usethis::use_readme_md()

# 뉴스 파일 생성 (업데이트 내역 관리)
usethis::use_news_md()
  • use_roxygen_md(): roxygen2를 사용하여 문서화를 설정합니다. 함수 주석에서 자동으로 문서를 생성할 수 있습니다.
  • use_readme_md(): 패키지의 README.md 파일을 생성하여 프로젝트 설명을 추가할 수 있습니다.
  • use_news_md(): 패키지 업데이트 내역을 기록하는 NEWS.md 파일을 생성합니다.

7. 라이선스 설정

패키지를 공개할 때 적절한 라이선스를 설정하는 것이 중요합니다. usethis는 다양한 라이선스 유형을 지원합니다.

# MIT 라이선스 추가
usethis::use_mit_license("Your Name")

# GPL-3 라이선스 추가
usethis::use_gpl3_license("Your Name")
  • use_mit_license(): MIT 라이선스를 추가합니다.
  • use_gpl3_license(): GPL-3 라이선스를 추가합니다.

8. 데이터 관리

패키지에 데이터를 포함하고 싶을 때, usethis를 사용하여 데이터를 쉽게 추가하고 문서화할 수 있습니다.

# 데이터를 R 패키지에 추가
usethis::use_data(my_data)

# 데이터에 대한 문서 생성
usethis::use_data_raw("my_data")
  • use_data(): 데이터를 패키지 내에 저장합니다 (data/ 디렉토리에 저장).
  • use_data_raw(): 원시 데이터를 저장하고 처리할 수 있는 파일을 생성합니다 (data-raw/ 디렉토리에 생성).

9. 버전 관리 및 배포

패키지 버전을 관리하고 CRAN 또는 GitHub에 배포하기 위해 필요한 파일을 자동으로 설정할 수 있습니다.

# CRAN에 제출할 수 있도록 CRAN 배포 환경 설정
usethis::use_cran_badge()

# GitHub Actions를 통한 자동화 설정
usethis::use_github_action("check-standard")
  • use_cran_badge(): CRAN 배포 상태 배지를 README.md에 추가합니다.
  • use_github_action(): GitHub Actions를 통해 R CMD check를 자동으로 실행할 수 있는 환경을 설정합니다.

요약

usethis 패키지는 R 패키지 개발을 매우 쉽게 해주는 도구입니다. 패키지 초기화부터 GitHub와의 연동, 테스트, 문서화, 배포에 이르기까지 대부분의 과정을 자동화하고 효율적으로 관리할 수 있습니다. 각 함수는 특정 작업을 쉽게 처리할 수 있도록 설계되어 있으며, 특히 패키지 개발 초보자에게 매우 유용합니다.

  • 패키지 초기화: create_package()
  • Git/GitHub 연동: use_git(), use_github()
  • 테스트: use_testthat(), use_test()
  • 문서화: use_roxygen_md(), use_readme_md()
  • 라이선스: use_mit_license(), use_gpl3_license()
  • 데이터 관리: use_data(), use_data_raw()
  • 배포: use_cran_badge(), use_github_action()

생산성 향상을 위한 나만의 R 패키지 빌드

roxygen2 패키지

roxygen2는 R 패키지 개발 시 문서화를 쉽게 해주는 패키지입니다. 함수 코드 위에 주석을 달아 설명을 작성하면, roxygen2는 이를 자동으로 패키지의 문서화 파일(.Rd 파일)을 생성하여 R 패키지 문서의 표준 형식에 맞게 정리해 줍니다. 이로 인해 개발자는 함수 설명을 별도의 문서 파일로 관리할 필요 없이 코드 주석만으로 문서를 작성할 수 있습니다.

roxygen2 패키지 사용법

1. roxygen2 패키지 설치 및 설정

먼저, roxygen2 패키지를 설치해야 합니다.

install.packages("roxygen2")

그 다음으로, R 패키지 프로젝트에서 roxygen2를 사용할 수 있도록 설정합니다.

# roxygen2를 패키지에 사용할 수 있도록 설정
usethis::use_roxygen_md()

이 명령은 DESCRIPTION 파일에서 Roxygen 관련 항목을 추가하고, roxygen2를 사용하여 문서를 관리할 수 있도록 설정합니다.

2. 함수 주석 작성

R 함수 위에 roxygen2 스타일로 주석을 작성하면 함수의 문서 파일을 자동으로 생성할 수 있습니다. roxygen2 주석은 #'으로 시작하며, 각 주석 블록은 특정 문서화 항목에 맞춰 작성됩니다.

예를 들어, 아래와 같은 R 함수가 있다고 가정합니다:

#' 두 수를 더하는 함수
#'
#' 이 함수는 두 숫자를 받아 더한 결과를 반환합니다.
#'
#' @param x 첫 번째 숫자
#' @param y 두 번째 숫자
#' @return 두 수의 합
#' @examples
#' add_numbers(1, 2)
add_numbers <- function(x, y) {
  return(x + y)
}
  • #': roxygen2 주석의 시작을 나타냅니다.
  • @param: 함수의 인수에 대한 설명을 제공합니다. 각 인수에 대해 하나씩 작성합니다.
  • @return: 함수가 반환하는 값을 설명합니다.
  • @examples: 함수 사용 예제를 제공합니다. 이 부분에 코드를 작성하면 문서에 포함됩니다.

3. 문서화 파일 생성

함수에 주석을 추가한 후, roxygen2를 사용하여 문서화 파일을 생성합니다. 다음 명령어를 실행하면 man/ 디렉토리 안에 .Rd 파일들이 생성됩니다.

# 문서화 생성
devtools::document()

이 명령은 함수의 주석을 기반으로 자동으로 문서 파일을 생성합니다. 생성된 문서 파일은 R 패키지 사용자들이 사용할 수 있는 함수의 도움말이 됩니다.

4. 문서화 주석 항목

roxygen2에서 자주 사용하는 주석 항목은 다음과 같습니다.

  • @param: 함수의 인수를 설명합니다.
  • @return: 함수가 반환하는 값을 설명합니다.
  • @examples: 함수 사용 예제를 제공합니다.
  • @export: 함수를 패키지의 외부 인터페이스로 내보낼 때 사용합니다. 이를 추가하면 함수가 패키지 설치 후 사용자에게 노출됩니다.
  • @import: 패키지에서 다른 패키지를 가져올 때 사용합니다.
  • @seealso: 관련된 함수나 문서로 연결할 때 사용합니다.
  • @author: 함수의 저자를 명시합니다.
  • @details: 함수에 대한 추가 세부 사항을 설명할 때 사용합니다.

5. 패키지에 여러 함수 문서화

패키지에 여러 함수를 정의하고 각각에 문서화를 추가할 수 있습니다. 각 함수에 대해 주석을 달아 문서화를 생성하고, 이 과정은 모든 함수에 대해 동일하게 적용됩니다.

예시:

#' 더하기 함수
#'
#' 두 수를 더합니다.
#' @param a 첫 번째 숫자
#' @param b 두 번째 숫자
#' @return 두 수의 합
#' @export
add <- function(a, b) {
  return(a + b)
}

#' 빼기 함수
#'
#' 두 수를 뺍니다.
#' @param a 첫 번째 숫자
#' @param b 두 번째 숫자
#' @return 첫 번째 숫자에서 두 번째 숫자를 뺀 결과
#' @export
subtract <- function(a, b) {
  return(a - b)
}

위의 두 함수에 대한 문서가 devtools::document()를 통해 자동으로 생성됩니다.

6. NAMESPACE 파일 자동 생성

roxygen2@export, @import 등의 주석을 통해 NAMESPACE 파일을 자동으로 관리합니다. NAMESPACE 파일은 패키지의 함수와 의존성을 정의하는 데 사용되며, 함수가 사용자에게 노출되거나 다른 패키지와의 연동이 이루어집니다.

7. @import@importFrom

다른 패키지의 함수를 패키지 내에서 사용할 때, @import 또는 @importFrom 태그를 사용하여 명시해야 합니다.

#' @import dplyr
#' @importFrom ggplot2 ggplot aes
  • @import dplyr: dplyr 패키지를 패키지 전반에 걸쳐 가져옵니다.
  • @importFrom ggplot2 ggplot aes: ggplot2 패키지에서 ggplotaes 함수만 가져옵니다.

8. 문서화와 테스트 자동화

roxygen2를 사용하면 문서화 작업이 단순해져 코드와 문서를 일관되게 유지할 수 있습니다. 특히 devtools::document() 명령어로 모든 문서를 자동으로 업데이트할 수 있어 편리합니다. 이 외에도 testthat과 연동하여 문서화와 테스트를 병행하면 더욱 신뢰성 있는 패키지를 개발할 수 있습니다.

요약

roxygen2는 R 패키지 개발 시 문서화를 간단하고 효율적으로 할 수 있도록 도와주는 도구입니다. 주석만으로 패키지의 문서를 관리할 수 있어 코드와 문서의 일관성을 유지할 수 있습니다. 주요 기능으로는 @param, @return, @examples 등의 태그를 통해 함수의 세부 정보를 자동으로 문서화하고, devtools::document() 명령을 사용해 패키지 문서를 쉽게 생성할 수 있습니다.

  • 설치: install.packages("roxygen2")
  • 주석 태그: @param, @return, @export, @examples, @import
  • 문서 생성: devtools::document()
  • 패키지 외부 함수 사용: @import, @importFrom

생산성 향상을 위한 나만의 R 패키지 빌드

devtools 패키지

devtools는 R 패키지 개발을 더 쉽게 해주는 필수적인 도구입니다. devtools는 패키지 개발, 테스트, 빌드, 문서화 및 배포 작업을 자동화하고 단순화하는 데 도움이 됩니다. 이 패키지를 사용하면 CRAN 표준을 준수하면서 R 패키지를 더 효율적으로 개발할 수 있습니다.

devtools 패키지 사용법

1. devtools 설치

먼저 devtools 패키지를 설치해야 합니다. CRAN에서 쉽게 설치할 수 있습니다.

install.packages("devtools")

설치가 완료되면 패키지를 로드하여 사용할 수 있습니다.

library(devtools)

2. 패키지 생성

devtools는 패키지를 쉽게 생성할 수 있는 함수인 create()를 제공합니다. 새로운 패키지를 만들고, 디렉토리 구조와 기본 파일들을 자동으로 생성해줍니다.

devtools::create("path/to/your/package")

이 명령어는 패키지 구조를 설정하고, DESCRIPTION 파일, NAMESPACE 파일, R/ 폴더 등 필요한 파일을 모두 만들어줍니다.

3. 패키지 빌드

패키지를 개발하는 중간에도 패키지를 테스트하거나 빌드할 수 있습니다. devtools::build() 명령어를 사용하여 패키지를 빌드할 수 있습니다. 이는 패키지를 완성하여 설치할 수 있는 상태로 만듭니다.

devtools::build()

이 명령어는 패키지를 압축된 .tar.gz 파일로 만듭니다.

4. 패키지 로드

패키지를 로컬 환경에서 설치하지 않고 테스트하려면 load_all() 함수를 사용할 수 있습니다. 이 함수는 패키지를 R의 개발 환경으로 바로 로드하여 함수 등을 바로 사용할 수 있게 합니다.

devtools::load_all()

5. 패키지 테스트

devtoolstestthat 패키지와 함께 테스트를 자동화할 수 있습니다. 패키지의 함수들이 예상대로 작동하는지 확인하기 위해 testthat을 설정하고 사용할 수 있습니다.

devtools::use_testthat()  # testthat 테스트 환경 설정
devtools::test()          # 모든 테스트 실행
  • use_testthat(): 패키지의 tests/ 디렉토리에 testthat 환경을 설정합니다.
  • test(): 작성된 테스트 스크립트를 실행하여 함수의 정확성을 검증합니다.

6. 패키지 문서화

devtoolsroxygen2 패키지와 연동되어 패키지의 함수에 주석을 달고 자동으로 문서를 생성할 수 있습니다. 이를 통해 함수의 설명과 사용 방법을 문서화합니다.

먼저, roxygen2 패키지를 사용하여 함수에 주석을 달고, 그 후 document() 명령어로 .Rd 파일을 생성합니다.

devtools::document()  # 문서화 파일 생성

이 명령어는 패키지 내 함수의 주석을 읽어 man/ 디렉토리에 .Rd 파일로 문서를 자동 생성합니다.

7. 패키지 설치

개발한 패키지를 로컬에서 테스트하기 위해 패키지를 설치할 수 있습니다.

devtools::install()

이 명령어는 패키지를 설치하고, R 세션에서 바로 사용할 수 있도록 로드합니다.

8. 코드 스타일 검사

devtools는 패키지의 품질과 코딩 스타일을 점검할 수 있는 도구도 제공합니다. lint() 함수는 코드를 점검하여 스타일 문제나 잠재적 오류를 찾아냅니다.

devtools::lint()

이 명령어는 R 코드에서 권장되지 않는 스타일이나 잠재적인 버그를 찾아줍니다. 하지만 각자의 스타일 있기 때문에 반드시 필요한 작업은 아닙니다.

9. 패키지 검사

이 함수는 CRAN에 제출되기 전에 모든 테스트를 통과하는지 확인하는 데 사용됩니다. 패키지의 품질과 호환성을 보장하기 위해 여러 가지 검사를 수행합니다.

devtools::check()

이 명령어는 CRAN 제출 적합성을 종합적으로 평가합니다.

10. 패키지 배포

패키지를 개발한 후, CRAN 또는 GitHub에 배포할 수 있습니다.

  • CRAN 배포: CRAN에 패키지를 제출하려면, release() 명령어를 사용합니다. 이 명령어는 패키지를 검사하고 배포할 준비가 되었는지 확인한 후, CRAN에 제출합니다.
devtools::release()
  • GitHub 배포: devtools를 사용하면 패키지를 쉽게 GitHub에 배포할 수 있습니다. 먼저 패키지를 GitHub에 저장한 후, install_github() 명령어를 사용하여 패키지를 GitHub에서 설치할 수 있습니다.
devtools::install_github("username/repository")

11. 패키지 의존성 관리

devtools는 패키지 의존성을 쉽게 관리할 수 있습니다. use_package() 명령어는 패키지의 DESCRIPTION 파일에 필요한 패키지를 자동으로 추가합니다.

devtools::use_package("dplyr")

이 명령어는 패키지가 dplyr에 의존하고 있음을 DESCRIPTION 파일에 명시합니다.

12. 예제 데이터 추가

패키지에 예제 데이터를 추가하려면, use_data() 함수를 사용하여 데이터를 패키지에 포함시킬 수 있습니다.

usethis::use_data(my_data)

이 명령어는 data/ 디렉토리에 데이터를 저장하고, 패키지에 내장된 데이터로 사용할 수 있게 합니다.

13. Git과 GitHub 연동

devtools는 Git과 GitHub 연동 작업도 쉽게 도와줍니다. use_git() 함수를 사용하여 Git을 초기화하고, use_github() 함수를 통해 패키지를 GitHub에 배포할 수 있습니다.

usethis::use_git()    # Git 초기화
usethis::use_github() # GitHub 저장소 생성 및 연동

14. 패키지 자동화 도구

devtools는 CI(Continuous Integration) 도구와 연동하여 패키지 빌드 및 테스트 작업을 자동화할 수 있습니다. GitHub Actions를 사용하여 R CMD check를 자동으로 실행할 수 있도록 설정할 수 있습니다.

usethis::use_github_action("check-standard")

이 명령어는 GitHub Actions에서 패키지 빌드 및 테스트가 자동으로 이루어지도록 설정합니다.

요약

devtools는 R 패키지 개발을 더 쉽고 효율적으로 만들어주는 도구입니다. 패키지 생성, 테스트, 문서화, 빌드, 설치, 배포 등의 과정을 자동화하고 간단하게 처리할 수 있습니다. 다음은 devtools 패키지의 주요 기능입니다:

  • 패키지 생성: create()
  • 패키지 빌드: build()
  • 패키지 로드: load_all()
  • 패키지 테스트: use_testthat(), test()
  • 패키지 문서화: document()
  • 패키지 설치: install()
  • 코드 스타일 검사: lint()
  • 패키지 검사: check()
  • 패키지 배포: release(), install_github()
  • 의존성 관리: use_package()
  • Git/GitHub 연동: use_git(), use_github()
  • CI 자동화: use_github_action("check-standard")

devtools는 패키지 개발에 필수적인 도구로, 복잡한 과정을 자동화하여 개발자가 패키지 로직에 더 집중할 수 있도록 돕습니다.

생산성 향상을 위한 나만의 R 패키지 빌드

testthat 패키지

testthat은 R 패키지 개발 중 테스트를 간편하게 작성하고 실행할 수 있도록 도와주는 패키지입니다. 패키지의 기능이 올바르게 작동하는지 확인하고, 코드 수정 후에도 동일한 결과를 유지하는지 보장하는 데 중요한 역할을 합니다. testthat 패키지는 특히 devtools 패키지와 함께 사용하면 편리합니다.

testthat 패키지의 사용법

1. testthat 패키지 설치

먼저 testthat 패키지를 설치하고 로드해야 합니다.

install.packages("testthat")
library(testthat)

2. 테스트 환경 설정

패키지 개발 시 testthat을 사용하여 테스트를 작성하려면 먼저 패키지 디렉토리에 tests/ 디렉토리와 testthat 환경을 설정해야 합니다. 이를 위해 devtools 또는 usethis 패키지를 사용할 수 있습니다.

# devtools 패키지를 사용하여 testthat 환경 설정
devtools::use_testthat()

# 또는 usethis 패키지를 사용할 수도 있습니다.
usethis::use_testthat()

이 명령어는 tests/testthat/ 디렉토리를 생성하고, R CMD check에서 테스트가 실행되도록 설정합니다. DESCRIPTION 파일에도 Suggests: testthat 항목이 추가됩니다.

3. 테스트 파일 생성

테스트를 작성할 때는 함수별로 테스트 파일을 작성하는 것이 일반적입니다. testthat의 파일명 규칙에 따라 테스트 파일명은 test-로 시작해야 합니다.

# devtools로 새로운 테스트 파일 생성
devtools::use_test("my_function")

위 명령어는 tests/testthat/test-my_function.R 파일을 생성합니다. 여기에서 my_function() 함수에 대한 테스트를 작성할 수 있습니다.

4. 테스트 작성

테스트 파일에서 test_that() 함수를 사용하여 테스트를 작성할 수 있습니다. 각 테스트는 하나의 논리적 단위를 담당하며, 기대하는 결과가 맞는지 확인합니다.

# test-my_function.R 파일 예시
test_that("my_function works correctly", {
  result <- my_function(1, 2)
  expect_equal(result, 3)  # 기대 결과와 비교
})
  • test_that(): 특정 테스트를 정의하는 함수입니다. 첫 번째 인수로는 테스트의 설명을, 두 번째 인수로는 테스트 코드 블록을 받습니다.
  • expect_equal(): 기대한 값과 실제 값을 비교합니다. 두 값이 동일하면 테스트가 성공합니다.

5. 다양한 기대(expect) 함수

testthat은 여러 가지 expect_*() 함수를 제공하여 다양한 유형의 검증을 할 수 있습니다. 주요 함수들은 다음과 같습니다:

  • expect_equal(): 두 값이 정확히 같은지 확인합니다.
  • expect_identical(): 두 값이 완전히 동일한 객체인지 확인합니다.
  • expect_true(): 표현식이 참인지 확인합니다.
  • expect_false(): 표현식이 거짓인지 확인합니다.
  • expect_error(): 특정 코드를 실행할 때 오류가 발생하는지 확인합니다.
  • expect_warning(): 경고가 발생하는지 확인합니다.
  • expect_message(): 특정 메시지가 출력되는지 확인합니다.

6. 테스트 실행

작성한 테스트를 실행하려면 다음 명령어를 사용할 수 있습니다:

# 현재 패키지의 모든 테스트 실행
devtools::test()

이 명령어는 tests/testthat/ 디렉토리에 있는 모든 테스트를 실행하고, 테스트 결과를 출력합니다. 테스트가 실패하면 실패한 이유를 출력해 줍니다.

7. 특정 파일에서 테스트 실행

특정 테스트 파일만 실행하려면 test_file()을 사용할 수 있습니다.

testthat::test_file("tests/testthat/test-my_function.R")

이 명령어는 지정한 파일의 테스트만 실행합니다.

8. 테스트 스킵 및 조건부 테스트

때로는 테스트를 특정 조건에서만 실행하거나 스킵하고 싶을 수 있습니다. skip_if()skip() 함수를 사용하여 이를 구현할 수 있습니다.

test_that("condition-based test", {
  skip_if(Sys.info()["sysname"] != "Linux")  # OS가 Linux가 아니면 테스트 스킵
  result <- my_function(1, 2)
  expect_equal(result, 3)
})
  • skip_if(): 특정 조건이 참일 때 테스트를 스킵합니다.
  • skip(): 테스트를 강제로 스킵합니다.

9. 의사 난수 생성기 테스트

확률적 또는 난수 기반의 함수 테스트 시 일관된 결과를 얻기 위해 의사 난수 생성기를 고정할 수 있습니다.

set.seed(123)
test_that("random function works consistently", {
  result <- my_random_function()
  expect_equal(result, expected_value)
})

set.seed()를 사용하여 난수 생성을 고정하면, 테스트 실행 시 일관된 결과를 얻을 수 있습니다.

10. 성능 테스트 (Benchmarking)

함수의 성능을 측정하고 싶다면 bench 패키지를 사용해 성능 테스트를 할 수 있습니다. bench::mark()를 사용하면 함수 실행 시간을 측정하고 성능 병목을 파악할 수 있습니다.

library(bench)
bench::mark(my_function(1, 2))

11. 테스트의 자동화 (Continuous Integration)

testthat 테스트는 GitHub Actions와 같은 CI(Continuous Integration) 도구와 연동하여 코드 푸시마다 자동으로 실행되도록 설정할 수 있습니다. 이를 통해 코드가 항상 정상 작동하는지 확인할 수 있습니다.

usethis::use_github_action("check-standard")

이 명령어는 GitHub Actions에 R CMD check와 함께 테스트가 자동으로 실행되도록 설정합니다.

요약

  • 설치 및 설정: install.packages("testthat"), devtools::use_testthat()
  • 테스트 파일 생성: devtools::use_test("function_name")
  • 테스트 작성: test_that()expect_*() 함수들을 사용하여 테스트 작성
  • 테스트 실행: devtools::test()를 사용해 패키지의 모든 테스트 실행
  • 기타 기능: 조건부 테스트 (skip_if()), 성능 테스트 (bench::mark()), 난수 고정 (set.seed())

testthat 패키지는 R 패키지 개발 시 신뢰성과 안정성을 확보하는 데 매우 유용합니다. 테스트를 작성하고 주기적으로 실행함으로써 코드의 품질을 유지하고, 패키지의 기능이 의도대로 작동하는지 확실하게 검증할 수 있습니다.

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발시 C/C++ 사용법

C 함수 사용

제가 사용하는 패키지 jaid의 일부를 활용해서 C 코드를 활용하여 성능을 최적화하는 방법에 대해 설명합니다. jaid.h, init.c, matrix.c, group.c 파일의 역할과 사용 방법에 대해 설명합니다.

목차

  1. jaid.h의 역할
  2. init.c의 역할
  3. matrix.c와 SumByColNames 함수
  4. matrix.R과 sum_by_colnames 함수
  5. group.c와 IndexOverlappingDateRange 함수
  6. overlap.R과 combine_overlapping_date_range 함수
  7. 요약

jaid.h의 역할

  • jaid.h는 C 코드에서 사용되는 함수의 선언을 포함하는 헤더 파일입니다.
  • 다른 .c 파일들이 이 파일을 통해 함수들을 호출할 수 있게 합니다.
  • URL: github.com/seokhoonj/jaid/blob/main/src/jaid.h
// jaid.h
#ifndef JAID_H
#define JAID_H

// 함수 선언
// Group
SEXP IndexOverlappingDateRange(SEXP id, SEXP from, SEXP to, SEXP interval); // 함수의 프로토타입
// Matrix
SEXP SumByColNames(SEXP x, SEXP g, SEXP uniqueg, SEXP snarm); // 함수의 프로토타입

#endif // JAID_H

init.c의 역할

  • init.c는 R과 C 함수를 연결하는 초기화 파일로, R에서 C 함수를 사용할 수 있도록 등록합니다.
  • R_registerRoutines() 함수를 사용하여 C 함수를 R 환경에 등록합니다.
  • URL: github.com/seokhoonj/jaid/blob/main/src/init.c
// init.c
#include <R_ext/Rdynload.h>
#include <R_ext/Visibility.h>
#include "jaid.h"

#define CALLDEF(name, n) {#name, (DL_FUNC) &name, n} // # is a stringify operator

static const R_CallMethodDef callEntries[] = {
  // Group
  CALLDEF(IndexOverlappingDateRange, 4),
  // Matrix
  CALLDEF(SumByColNames, 4),
  {NULL, NULL, 0}
};

void attribute_visible R_init_jaid(DllInfo *info) {
  R_registerRoutines(info, NULL, callEntries, NULL, NULL);
  R_useDynamicSymbols(info, FALSE);
  R_forceSymbols(info, TRUE);
}

matrix.c의 SumByColNames 함수

  • matrix.c 파일은 행렬 연산을 수행하는 함수들을 구현합니다.
  • SumByColNames 함수는 주어진 행렬에서 특정 열 이름을 기준으로 합계를 계산합니다.
  • URL: github.com/seokhoonj/jaid/blob/main/src/matrix.c
// matrix.c
#include "jaid.h"

// 미리 정의 된 부분 (Hashing 관련)
...
...
...

// SumByColNames 함수 정의
SEXP SumByColNames(SEXP x, SEXP g, SEXP uniqueg, SEXP snarm) {
  SEXP matches, z;
  int n, p, ng, narm;
  HashData data = { 0 };
  data.nomatch = 0;

  n = LENGTH(g);
  ng = length(uniqueg);
  narm = asLogical(snarm);
  if (narm == NA_LOGICAL) error("'na.rm' must be TRUE or FALSE");
  if (isMatrix(x)) p = nrows(x); else p = 1;

  HashTableSetup(uniqueg, &data, NA_INTEGER); // 미리정의
  PROTECT(data.HashTable); // 미리 정의
  DoHashing(uniqueg, &data); // 미리 정의
  PROTECT(matches = HashLookup(uniqueg, g, &data)); // 미리 정의
  int *pmatches = INTEGER(matches);

  PROTECT(z = allocMatrix(TYPEOF(x), p, ng));

  switch(TYPEOF(x)){
  case INTSXP:{
    Memzero(INTEGER(z), p*ng);
    int* iz = INTEGER(z);
    int* ix = INTEGER(x);
    for (int i = 0; i < n; i++) {
      int colz = (pmatches[i] - 1) * p;
      int colx = i*p;
      for (int j = 0; j < p; j++) {
        iz[colz + j] += ix[colx + j];
      }
    }
  } break;
  case REALSXP:{
    Memzero(REAL(z), p*ng);
    double* iz = REAL(z);
    double* ix = REAL(x);
    for (int i = 0; i < n; i++) {
      int colz = (pmatches[i] - 1) * p;
      int colx = i*p;
      for (int j = 0; j < p; j++) {
        iz[colz + j] += ix[colx + j];
      }
    }
  } break;
  default:
    error("non-numeric matrix in row_sum_by_cn(): this should not happen");
  }
  if (TYPEOF(uniqueg) != STRSXP) error("row names are not character");
  SEXP dn = allocVector(VECSXP, 2), dn2, dn3;
  setAttrib(z, R_DimNamesSymbol, dn);
  SET_VECTOR_ELT(dn, 1, uniqueg);
  dn2 = getAttrib(x, R_DimNamesSymbol);
  if (length(dn2) >= 2 && !isNull(dn3 = VECTOR_ELT(dn2, 0)))
    SET_VECTOR_ELT(dn, 0, dn3);
  UNPROTECT(3); /* HashTable, matches, z */
  return z;
}

matrix.R과 sum_by_colnames 함수

# matrix.R

sum_by_colnames <- function(x, na.rm = TRUE) {
  g <- colnames(x); uniqueg <- unique(g)
  .Call(SumByColNames, x, g, uniqueg, na.rm)
}

group.c의 IndexOverlappingDateRange 함수

  • group.c는 날짜 범위가 겹치는 인덱스를 찾는 IndexOverlappingDateRange 함수를 제공합니다.
  • 이 함수는 시간 범위의 오류가 있는 데이터에서 중복을 제거하는데 유용합니다.
// group.c

#include "jaid.h"

/* "loc" means numbers to be grouped. if the loc vector is like
 * c(1, 1, 1, 2, 2, 2, 2), we got two groups first 3 rows and second 4 rows.
 * "sub" means subtracting number of days when the interval argument is longer
 * than 0. if the two date ranges are like "2014-02-03 ~ 2014-02-04" and
 * "2014-02-12 ~ 2014-02-13" and the interval is 7, it is combined as 2014-02-03 ~ 2014-02-13 */
SEXP IndexOverlappingDateRange(SEXP id, SEXP from, SEXP to, SEXP interval) {
  R_xlen_t m, n, i, j;
  SEXP loc, sub, v, z;
  if (isVectorList(id)) {
    m = XLENGTH(VECTOR_ELT(id, 0)), n = XLENGTH(id);
  } else {
    m = XLENGTH(id), n = 1;
  }

  // type of date is double
  double *ifr = REAL(from);
  double *ito = REAL(to);
  // interval is integer
  double vinterval = asReal(interval);

  PROTECT(loc = allocVector(INTSXP, m));
  PROTECT(sub = allocVector(INTSXP, m));
  FillCInt(loc, 1);
  FillCInt(sub, 0);
  int *iloc = INTEGER(loc);
  int *isub = INTEGER(sub);

  int p = 1, mx = 0; // index, maximum `to`
  bool c1, c2; // condition 1, condition 2
  for (i = 1; i < m; ++i) {
    j = 0, c1 = true;
    while (j < n) {
      if (isVectorList(id)) {
        v = VECTOR_ELT(id, j);
      } else {
        v = id;
      }
      switch(TYPEOF(v)){
      case LGLSXP:{
        int *iv = LOGICAL(v);
        c1 = (iv[i-1] == iv[i]);
      } break;
      case INTSXP:{
        int *iv = INTEGER(v);
        c1 = (iv[i-1] == iv[i]);
      } break;
      case REALSXP:{
        double *iv = REAL(v);
        c1 = (iv[i-1] == iv[i]);
      } break;
      case STRSXP:{
        SEXP *iv = STRING_PTR(v);
        c1 = (!strcmp(CHAR(iv[i-1]), CHAR(iv[i])));
      } break;
      default:
        error(_("invalid input"));
      }
      if (c1 == false) break;
      j++;
    }
    mx = (ito[i-1] > mx) ? ito[i-1] : mx;
    c2 = ifr[i] <= (mx + 1 + vinterval);
    if (c1 && c2) {
      iloc[i] = p;
      if (ifr[i] > mx) isub[i] = ifr[i] - mx - 1;
    } else {
      iloc[i] = ++p;
      mx = ito[i];
    }
  }
  const char *names[] = {"loc", "sub", ""};
  PROTECT(z = mkNamed(VECSXP, names));
  SET_VECTOR_ELT(z, 0, loc);
  SET_VECTOR_ELT(z, 1, sub);
  UNPROTECT(3);
  return z;
}

overlap.R과 combine_overlapping_date_range 함수

# overlap.R

combine_overlapping_date_range <- function(df, id_var, merge_var, from_var, to_var,
                                           interval = 0, collapse = "|") {
  id_var    <- match_cols(df, sapply(rlang::enexpr(id_var), rlang::as_name))
  merge_var <- match_cols(df, sapply(rlang::enexpr(merge_var), rlang::as_name))
  from_var  <- rlang::as_name(rlang::enquo(from_var))
  to_var    <- rlang::as_name(rlang::enquo(to_var))
  all_var   <- c(id_var, merge_var, from_var, to_var)
  dt <- df[, .SD, .SDcols = all_var]
  data.table::setnames(dt, c(id_var, merge_var, "from", "to"))
  data.table::setorderv(dt, c(id_var, "from", "to"))
  data.table::set(dt, j = "sub_stay", value = 0)
  index <- .Call(IndexOverlappingDateRange, dt[, .SD, .SDcols = id_var],
                 dt$from, dt$to, interval = interval) // C 함수 호출
  data.table::set(dt, j = "loc", value = index$loc) # group index to combine
  data.table::set(dt, j = "sub", value = index$sub) # days to subtract, if the interval is longer than 0
  group_var <- c(id_var, "loc")
  m <- dt[, lapply(.SD, function(x) paste(unique(x[!is.na(x)]), collapse = collapse)),
          keyby = group_var, .SDcols = merge_var]
  from <- to <- sub_stay <- sub <- NULL
  s <- dt[, list(from = min(from), to = max(to), sub_stay = sum(sub_stay) + sum(sub)),
          keyby = group_var]
  z <- m[s, on = group_var]
  data.table::set(z, j = "loc", value = NULL)
  data.table::set(z, j = "stay", value = as.numeric(z$to - z$from + 1 - z$sub_stay))
  data.table::set(z, j = "sub_stay", value = NULL)
  data.table::setnames(z, c(all_var, "stay"))
  return(z)
}

요약

C 코드는 R의 성능을 극대화합니다. jaid.h, init.c, functions.c, functions.R 파일을 통해 R과 C 간의 상호작용을 효과적으로 구현할 수 있습니다. 참고로, sum_by_colnames와 같은 기능을 하는 기초함수가 없고, combine_overlapping_date_range와 같은 기능을 R로 구현하면 데이터의 크기에 따라 엄청난 속도 차이를 보이게 됩니다.

C++ 사용

이 문서는 R 패키지에서 C++ 코드를 활용하여 성능을 최적화하는 방법에 대해 설명합니다. RcppExports.cppadd.cpp 파일의 역할, 그리고 cppAdd 함수가 R과 어떻게 연결되는지를 다룹니다.

목차

  1. RcppExports.cpp의 역할
  2. add.cpp와 cppAdd 함수
  3. R에서 cppAdd 함수 호출
  4. Makevars 파일
  5. 요약

RcppExports.cpp의 역할

  • 자동 생성 파일: RcppExports.cppRcpp 패키지가 자동으로 생성하는 파일로, C++ 함수와 R 간의 인터페이스를 설정합니다.
  • Rcpp Attributes 사용: Rcpp는 함수 위에 // [[Rcpp::export]]와 같은 주석을 통해 R과 C++ 간의 연결을 설정합니다. 이 주석을 통해 Rcpp는 C++ 함수를 R에서 호출할 수 있도록 필요한 코드를 자동으로 생성합니다.
  • 함수 등록 및 매핑: RcppExports.cpp는 패키지 초기화 시 init.c와 유사하게 C++ 함수가 R에서 호출될 수 있도록 등록합니다.

RcppExports.cpp의 예시

// RcppExports.cpp
#include <Rcpp.h>
using namespace Rcpp;

// cppAdd를 포함한 C++ 함수가 이 파일에서 정의되어 R과 연결됨

// Rcpp::export 주석을 사용해 자동으로 R과 C++ 함수 연결을 설정
// [[Rcpp::export]]
int cppAdd(int x, int y) {
    // C++ 구현부는 add.cpp에서 정의된 함수 호출
    return cppAdd_internal(x, y);
}

add.cpp와 cppAdd 함수

  • 간단한 예제: add.cpp는 R의 기본 함수보다 빠른 속도를 제공하는 C++ 함수들을 구현하는 파일입니다. 특히, cppAdd 함수는 두 정수를 더하는 간단한 기능을 제공합니다.
  • 내부 구현: cppAdd는 실제 계산을 수행하는 함수로, 간단한 덧셈 작업을 수행합니다.

add.cpp에서 cppAdd의 구현 예시

// cppAdd.cpp
#include <Rcpp.h>
using namespace Rcpp;

// cppAdd 함수의 실제 구현
int cppAdd(int x, int y) {
    // 두 수를 더하여 반환
    return x + y;
}

R에서 cppAdd 함수 호출

R에서는 Rcpp가 생성한 RcppExports.cpp 덕분에 C++ 함수 cppAdd를 간단하게 호출할 수 있습니다.

# R에서 C++ 함수 호출 예시
library(jaid)

# 두 숫자를 더하기
result <- cppAdd(3, 4)
print(result)  # 출력: 7

Makevars 파일

  • Makevars는 C/C++ 코드의 컴파일을 제어하는 설정 파일입니다.

Makevars 예시

# Makevars
# R 3.1.0 이상에서 C++11을 사용하도록 설정
CXX_STD = CXX11

# OpenMP 지원 플래그
PKG_CXXFLAGS = $(SHLIB_OPENMP_CXXFLAGS)

# 사용하는 라이브러리 설정
PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)

요약

  1. RcppExports.cpp: Rcpp에 의해 자동으로 생성되어 C++ 함수와 R 간의 연결을 설정합니다. 이 파일은 Rcpp::export 주석을 기반으로 R과 C++ 함수 간의 매핑을 설정합니다.
  2. add.cppcppAdd 함수: C++로 간단한 덧셈을 수행하는 함수로, R의 기본 계산 함수보다 빠르게 동작하도록 설계되었습니다.
  3. R과의 통합: Rcpp 덕분에 R 사용자들은 C++로 작성된 고성능 함수를 손쉽게 사용할 수 있으며, 데이터 처리 속도를 대폭 향상시킬 수 있습니다.

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발시 Python 사용법

R 패키지 개발 시 Python의 클래스를 활용하여 기능을 구현하고, 이를 R과 통합할 수 있습니다. 이는 R이 기본적으로 제공하는 기능의 확장을 가능하게 하며, 특히 복잡한 알고리즘이나 데이터 처리 작업을 파이썬으로 구현하고, 이를 R에서 쉽게 사용할 수 있도록 합니다. 여기서는 papagor 패키지를 참고하여, R 패키지에서 파이썬 클래스를 사용하는 방법을 설명합니다. - papagor: github.com/seokhoonj/papagor/

1. R과 Python 통합을 위한 reticulate 패키지

reticulate는 R과 Python을 통합하는 R 패키지로, Python의 객체를 R에서 직접 사용할 수 있게 합니다. reticulate를 사용하면 Python에서 정의한 클래스와 메서드를 R 환경 내에서 호출하고 조작할 수 있습니다.

2. Python 클래스 작성

papagor 패키지에서 사용할 Python 클래스를 작성합니다.

# translator.py

# NAVER Papago Text Translation API
import urllib.request
import json
import numpy as np
import pandas as pd
from numbers import Number

class Translator:
    '''papago translator'''
    def __init__(self, client_id, client_secret, source, target, platform = False):
        self.client_id = client_id
        self.client_secret = client_secret
        self.source = source
        self.target = target
        self.platform = platform

    def translate_text(self, text):
        '''simple translator'''
        ...
        return translated_text
        
    def translate(self, text):
        '''generalized translator'''
        ...
        return translated
        
    ...

이 Python 파일을 papagor/inst/python/papago/translator.py로 저장합니다. 그리고 최종적으로는 다음과 같은 구조를 갖게 됩니다.

alt text

Python 클래스를 활용할 R 함수 작성

# translate.R

translate <- function(text, source, target, platform = TRUE) {
  client_id <- get_client_id()
  client_secret <- get_client_secret()
  if (missing(source))
    source <- get_source_lang()
  if (missing(target))
    target <- get_target_lang()
  translator <- papago$translator$Translator(
    client_id = client_id,
    client_secret = client_secret,
    source = source,
    target = target,
    platform = platform
  )
  return(translator$translate(texts = text))
}

papagor 패키지 로드 시 Python 클래스를 불러올 코드 작성

# zzz.R

papago <- NULL
.onLoad <- function(libname, pkgname) {
  papago <<- reticulate::import_from_path(
    module = "papago",
    path = system.file("python", package = "papagor"),
    delay_load = TRUE
  )
}

실제로 papagor을 로드하면 다음과 같이 papago 클래스에 접근할 수 있습니다.

> library(papagor)
# Please register your app and get your client id and secret from website 'https://developers.naver.com/apps/#/register'.

> papagor:::papago
# Module(papago) # module

> papagor:::papago$translator
# Module(papago.translator) # module

> papagor:::papago$translator$Translator
# <class 'papago.translator.Translator'> # class

> papagor:::papago$translator$Translator$translate
# <function Translator.translate at 0x000000000000> # function

이제 다음과 같이 R에서 Python 클래스를 이용한 함수를 사용할 수 있게 됩니다.

papagor::set_lang_env(source = "en", target = "ko")
papagor::translate("Hello")
# > 안녕하세요

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발시 ggplot2 사용법

R 패키지 개발 시 ggplot2를 이용한 함수를 작성할 때, 우리는 먼저 rlang 패키지에 대해서 이해해야 합니다. rlang 패키지는 변수의 범위와 참조를 안전하게 관리할 수 있도록 돕는 중요한 도구입니다. 특히, enquo(), enquos(), !!, !!! 함수는 비표준 평가(Non-standard evaluation, NSE)를 안전하게 다룰 수 있게 해주어, ggplot2와 같은 그래픽 함수와의 통합을 쉽게 만듭니다.
R을 활용하면서 기초함수에서 가장 빈번하게 나오는 비표준 평가 예시는 deparse(substitute(x)) 입니다. 하지만 이 표현을 재사용성을 위해 함수로 만들고 다른 함수안에서 사용하게 되면 그냥 x가 나오는 황당한 경험을 하게 됩니다. 그래서 이를 해결하기 위해 제가 아주 오래전 고안하게 된 표현은 다음과 같은 복잡한 형태였습니다.

f <- function(x) {
  print(deparse(substitute(x)))
}

# > f(t)
# [1] "t"

desub1 <- function(x) deparse(substitute(x))
f1 <- function(x) {
  print(desub1(x))
}

# > f1(t)
# [1] "x"

desub2 <- function(x) {
    deparse(eval(eval(eval(eval(eval(eval(eval(eval(eval(eval(eval(eval(eval(
    substitute(substitute(substitute(substitute(substitute(substitute(substitute(substitute(substitute(substitute(substitute(substitute(substitute(substitute(x)))))))))))))), 
        envir = parent.frame(n = 1)), envir = parent.frame(n = 2)), 
        envir = parent.frame(n = 3)), envir = parent.frame(n = 4)), 
        envir = parent.frame(n = 5)), envir = parent.frame(n = 6)), 
        envir = parent.frame(n = 7)), envir = parent.frame(n = 8)), 
        envir = parent.frame(n = 9)), envir = parent.frame(n = 10)), 
        envir = parent.frame(n = 11)), envir = parent.frame(n = 12)), 
        envir = parent.frame(n = 13)))
}
f2 <- function(x) {
  print(desub2(x))
}

# > f2(t)
# [1] "t"

참고로 desub2 함수는 일반적인 R 환경에서는 잘 되지만, Shiny Application에서는 또다른 namespace를 고려해야 하므로 생각한대로 작동하지 않습니다. 그런 측면에서 rlang은 R 생태계에서 굉장히 훌륭한 대안으로서의 역할을 하게 된 것입니다.

rlang의 역할

  • 비표준 평가(NSE) 처리: ggplot2와 같은 그래픽 패키지들은 변수 이름을 직접 인자로 받아 사용하는 비표준 평가 방식을 사용합니다. 이 때, rlang 패키지의 enquo()enquos()를 사용하여 함수 내에서 변수를 안전하게 참조하고, 코드가 의도한 대로 동작하도록 할 수 있습니다.
  • 동적 변수 처리: 사용자가 지정한 변수 이름을 함수 내에서 동적으로 인식하고 사용할 수 있게 해줍니다.
  • 명확한 오류 처리: 인자를 quosure로 변환하여, 변수의 유효성을 쉽게 검증할 수 있도록 돕습니다.

enquo()enquos() 사용법

1. enquo()

enquo()는 단일 인자를 quosure로 변환하여 함수 내부에서 안전하게 참조할 수 있게 만듭니다. 이는 특히 ggplot2와 같은 함수 내에서 변수 이름을 다룰 때 유용합니다. 참고로 quosure는 코드 표현(expression)과 해당 코드가 평가될 환경(environment)을 묶어 놓은 객체입니다. 이를 통해 코드가 정의된 시점의 환경을 기억하여, 코드가 올바른 환경에서 평가될 수 있게 합니다.

사용 예시

아래는 enquo()를 사용하여 ggplot2의 함수에서 동적으로 변수를 매핑하는 예시입니다.

# 예제 함수: 단일 변수를 ggplot에서 사용
plot_single_variable <- function(data, x_var) {
  # 인자를 quosure로 변환
  x_var <- enquo(x_var)
  
  # ggplot2 사용 시 변수를 안전하게 매핑
  ggplot(data, aes(x = !!x_var)) +
    geom_histogram(binwidth = 1) +
    theme_minimal()
}

# 함수 사용 예시
library(ggplot2)
plot_single_variable(mtcars, mpg)

2. enquos()

enquos()는 여러 개의 인자를 받아 quosure의 리스트로 변환합니다. 이는 다수의 변수를 받아 처리할 때 유용하며, ggplot2에서 여러 변수를 동적으로 참조할 수 있게 합니다.

사용 예시

아래는 enquos()를 사용하여 다수의 변수를 ggplot의 aes()에 동적으로 매핑하는 함수입니다.

# 예제 함수: 여러 변수를 ggplot에서 사용
plot_multiple_variables <- function(data, ...) {
  # 인자를 quosure 리스트로 변환
  vars <- enquos(...)
  
  # 첫 번째 인자는 x 축, 두 번째 인자는 y 축으로 매핑
  ggplot(data, aes(!!vars[[1]], !!vars[[2]])) +
    geom_point() +
    theme_minimal()
}

# 함수 사용 예시
plot_multiple_variables(mtcars, mpg, wt)

3. !! (Bang-Bang 연산자)

  • 역할: !! 연산자는 quosure로 인용된 인자를 평가하여 실제 값으로 변환합니다. 이는 ggplot2와 같은 함수에서 변수를 동적으로 참조할 때 필요합니다.

4. !!! (Bang-Bang-Bang 연산자)

  • 역할: !!! 연산자는 여러 개의 인자나 리스트를 펼쳐서 함수에 전달할 때 사용됩니다. 주로 enquos()와 함께 사용되며, 여러 개의 quosure를 한 번에 풀어서 함수에 전달합니다.

사용 예시

아래 예시는 !!! 연산자를 사용하여 aes() 함수에 여러 변수를 동적으로 전달하는 방법을 보여줍니다.

# 예제 함수: 여러 변수를 ggplot에서 사용
plot_with_multiple_aes <- function(data, ...) {
  # 인자를 quosure 리스트로 변환
  vars <- enquos(...)
  
  # !!! 연산자를 사용하여 인자를 한 번에 펼쳐서 aes에 전달
  ggplot(data, aes(!!!vars)) +
    geom_point() +
    theme_minimal()
}

# 함수 사용 예시
plot_with_multiple_aes(mtcars, mpg, wt)

5. 요약

  • enquo(): 단일 변수를 quosure로 변환하여 안전하게 참조.
  • enquos(): 다수의 변수를 quosure 리스트로 변환하여 동적으로 매핑.
  • !!: quosure를 평가하여 실제 값을 반환.
  • !!!: 여러 quosure를 한 번에 풀어서 함수에 전달.

개발 예시

library(ggplot2)
library(rlang)

ggbar <- function(data, x, y, ymin = NULL, ymax = NULL, ymin_err, ymax_err, 
    group = NULL, fill = NULL, text = NULL, bar_color = "transparent", 
    label, label_family = "Comic Sans MS", label_size = 4, label_angle = 0, 
    label_hjust = 0.5, label_vjust = 0.5, label_color = c("#000000", 
        "#FAF9F6")) {
    quo_maps <- rlang::enquos(x = x, y = y, ymin = ymin, ymax = ymax, 
        group = group, fill = fill, text = text)
    quo_maps <- quo_maps[!sapply(quo_maps, rlang::quo_is_null)]
    ggplot(data = data, aes(!!!quo_maps)) + geom_bar(stat = "identity", 
        position = position_dodge2(preserve = "single"), color = bar_color) + 
        list(if (!(missing(ymin_err) & missing(ymax_err))) {
            quo_errs <- rlang::enquos(x = x, ymin = ymin_err, 
                ymax = ymax_err)
            geom_errorbar(aes(!!!quo_errs), position = position_dodge2(preserve = "single"), 
                alpha = 0.5)
        }) + list(if (!missing(label)) {
        quo_lbl <- rlang::enquos(label = label)
        geom_text(aes(!!!quo_lbl), position = position_dodge2(width = 0.9, 
            preserve = "single"), family = label_family, size = label_size, 
            angle = label_angle, hjust = label_hjust, vjust = label_vjust, 
            color = label_color[1L])
    })
}

if (!require("insuranceData")) install.packages("insuranceData")

library(insuranceData)

data("AutoCollision")
head(AutoCollision)
#>   Age Vehicle_Use Severity Claim_Count
#> 1   A    Pleasure   250.48          21
#> 2   A  DriveShort   274.78          40
#> 3   A   DriveLong   244.52          23
#> 4   A    Business   797.80           5
#> 5   B    Pleasure   213.71          63
#> 6   B  DriveShort   298.60         171

ggbar(AutoCollision, x = Age, y = Claim_Count, fill = Vehicle_Use) + 
  labs(title = "Auto Collision") +
  theme_view()

alt text

결론

rlang 패키지의 enquo(), enquos(), !!, !!!는 R 패키지 개발 시 ggplot2와의 통합을 매끄럽게 만들어줍니다. 이를 통해 사용자가 지정한 변수를 함수 내에서 동적으로 안전하게 처리할 수 있으며, 코드의 가독성과 유지보수성을 높일 수 있습니다.

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발 시 Shiny 사용법

1. Shiny Module

Shiny 모듈화는 코드 재사용성을 높이고 유지보수를 쉽게 만드는데 필수적입니다. 다음은 selectInput 드롭다운 메뉴에 대한 설명을 그 예로 하고 있습니다. 기본적으로 ui 함수에 적용될 selectUI와 server 함수에 포함될 dynSelectServer로 이루어져 있습니다. selectServer가 아닌 dynSelectServer인 이유는 인수에 따라 Reactive하게 움직이기 때문입니다.

# selectInput의 Module UI (드롭다운 메뉴)
selectUI <- function(id, label = "Select", choices = NULL, selected = NULL, 
                     multiple = FALSE) {
    ns <- NS(id) # 모듈의 네임스페이스 설정
    tagList(selectInput(ns("select"), label = label, choices = choices, 
            selected = selected, multiple = multiple))
}

# 특정 컬럼의 유니크한 값들이 selectInput의 값이 되는 Module Server
# 여기서 인수가 되는 data는 반드시 reactive가 적용된 데이터여야 합니다.
dynSelectServer <- function(id, data, column, selected = NULL, reverse = FALSE) {
    moduleServer(id, function(input, output, session) {
        ns <- session$ns # 모듈의 네임스페이스 생성
        observeEvent(data(), {
            dt <- data()
            choices <- sort(unique(dt[[column]]))
            if (reverse) 
                choices <- rev(choices)
            updateSelectInput(session, inputId = "select", choices = choices, 
                selected = selected)
        })
        select <- reactive({
            validate(need(input$select, message = FALSE))
            input$select
        })
        return(select)
    })
}
ui <- fluidPage(
  fluidRow(
    column(width =  4, selectUI(id = "new_id1")),
    column(width =  4, selectUI(id = "new_id2")),
    column(width =  4, selectUI(id = "new_id3"))
  )
)

server <- function(input, output, session) {
    data <- reactive(mtcars)
    new_id1 <- dynSelectServer(id = "new_id1", data = data(), selected = "vs")
    new_id2 <- dynSelectServer(id = "new_id2", data = data(), selected = "am")
    new_id3 <- dynSelectServer(id = "new_id2", data = data(), selected = "cyl")
}

shinymodules selectUI & dynSelectServer

2. 함수에서 Shiny App 실행

Module화가 진행되고 나면 Shiny 코드를 비교적 쉽게 해체하고 재사용 할 수 있습니다. 다음은 CRAN의 Top 다운로드 패키지를 알아보는 간단한 Shiny App 함수의 예시입니다.

plot_cran_top <- function(when = c("last-month", "last-week", "last-day"), 
                          count = 10, viewer_width = 1200, viewer_height = 500) {
  when <- match.arg(when)
  shiny::runGadget(
    shiny::fluidPage(
      shiny::plotOutput("cran_download_plot", width = "100%", height = "500px")
    ),
    function(input, output, session) {
      output$cran_download_plot <- renderPlot({
        cran_tops <- cranlogs::cran_top_downloads(when = when, count = count)
        cran_tops$rank <- as.factor(cran_tops$rank)
        ggshort::ggbar(cran_tops, x = rank, y = count, fill = rank, 
                       label = package, label_size = 5, label_hjust = -.1) +
          ggshort::scale_y_comma() +
          coord_flip() +
          ggshort::scale_x_limit_reverse(cran_tops$rank) +
          labs(title = sprintf("Cran Top Downloads (%s)", when)) +
          ggshort::theme_shiny(x.angle = 45)
      })
    }, viewer = shiny::dialogViewer(sprintf("Cran Top Downloads %s", when), 
                                    width = viewer_width, height = viewer_height))
}

plot_cran_top()

alt text

생산성 향상을 위한 나만의 R 패키지 빌드

R 패키지 개발 참고 자료

R 패키지 개발에 도움이 되는 C, C++, Shiny 모듈 개발 관련 웹사이트와 문서를 정리한 자료입니다. R 패키지의 성능 향상을 위해 C/C++ 통합, Shiny 모듈화를 포함한 고급 개발 기술을 다룹니다.

R 패키지 개발 가이드

  1. R Packages by Hadley Wickham

    • URL: r-pkgs.org
    • 설명: R 패키지 개발을 위한 가이드북입니다. 패키지 구조, 문서화, 테스트, 배포까지의 모든 과정을 상세하게 설명합니다.

  2. Advanced R by Hadley Wickham

    • URL: adv-r.hadley.nz
    • 설명: R의 고급 기능과 프로그래밍 개념을 설명하며, 패키지 개발의 심화 내용을 다룹니다.

  3. CRAN R Packages

    • URL: cran.r-project.org/web/packages
    • 설명: R 패키지의 공식 리포지토리로, 현재 배포된 모든 패키지와 관련 문서를 제공합니다.

C와 C++

  1. R source

    • URL: github.com/wch/r-source/tree/trunk/src/main
    • 설명: 해당 Git repository를 통해 R의 기본적인 소스 구조를 이해할 수 있습니다. 개인적으로는 가장 많이 참고하는 자료입니다.

  2. R internals

    • URL: github.com/hadley/r-internals
    • 설명: 해들리 위컴이 본인의 Git repository에서 R의 내부구조에 대해 설명한 자료입니다.

  3. R’s C interface

  4. KIT

    • URL: teuder.github.io/rcpp4everyone_en
    • 설명: Base R에서 제공되지 않은 것 포함 C로 구현된 기본 함수들의 소스 코드를 제공하고 있습니다. Base R 소스 코드보다 더 직관적인 느낌입니다.

  5. Rcpp

    • URL: rcpp.org
    • 설명: C++ 코드와 R을 통합하여 R 패키지의 성능을 크게 향상시킬 수 있는 Rcpp 패키지에 대한 공식 사이트입니다. 다양한 예제와 가이드가 제공됩니다.

  6. Writing R Extensions

  7. Rcpp Gallery

    • URL: gallery.rcpp.org
    • 설명: Rcpp를 활용한 다양한 예제와 코드 스니펫을 제공하여, R과 C++ 통합의 실전 활용 방법을 배울 수 있습니다.

Shiny 모듈 개발

  1. Mastering Shiny by Hadley Wickham

    • URL: mastering-shiny.org
    • 설명: Shiny 앱의 모듈화를 포함하여 Shiny 앱 개발을 심층적으로 다룬 가이드입니다. Shiny 모듈 개발에 필요한 구조와 패턴을 배울 수 있습니다.

  2. Shiny Modules Tutorial

    • URL: shiny.rstudio.com/articles/modules.html
    • 설명: Shiny 모듈을 사용하여 복잡한 앱을 더 쉽게 관리하고 재사용성을 높이는 방법을 설명하는 공식 튜토리얼입니다.

  3. Shiny Developer Series

    • URL: rstudio.github.io/shinydevseries
    • 설명: Shiny 앱 개발에 유용한 팁과 고급 기술을 다루며, 모듈화된 Shiny 앱 구축을 위한 가이드와 비디오를 제공합니다.

테스트 및 CI/CD

  1. usethis Package

    • URL: usethis.r-lib.org
    • 설명: R 패키지 개발을 쉽게 만들어주는 usethis 패키지에 대한 문서입니다. 초기 설정부터 문서화, 테스트, 배포까지 자동화 도구를 제공합니다.

  2. testthat Package

    • URL: testthat.r-lib.org
    • 설명: R의 가장 널리 사용되는 테스트 프레임워크로, 패키지 기능 테스트에 필수적입니다.

  3. GitHub Actions for R

기타

  1. ChatGPT