Research projects and data analyses can be hard to manage. These problems are common:
The org package solves these problems. It provides a
standardized framework that organizes R projects. The framework gives
you a clear separation of concerns, and a consistent structure across
all your analyses.
The code below starts your first org project.
library(org)
# 1. Initialize your project structure
org::initialize_project(
env = .GlobalEnv,
home = "my_analysis",
results = "my_results"
)
# 2. Access project paths
org::project$home # Your code location
org::project$results_today # Today's results folder
# 3. Use org functions in your analysis
org::path("data", "file.csv") # Cross-platform paths
org::ls_files("R") # List R filesThe concept behind org is straightforward. Most analyses
have three main sections:
Each section has unique requirements:
org::initialize_projectinitialize_project() is the main function that sets up
your project structure. It takes two or more arguments. It saves folder
locations in org::project for use throughout your
analysis:
home: Location of Run.R and the
R/ folder (accessible via
org::project$home).results: Results folder that creates date-based
subfolders (accessible via
org::project$results_today)....: Additional folders as needed (e.g.,
data_raw, data_clean).max_loc_per_file: The largest number of code lines one
.R file may hold. initialize_project() stops with an error
naming every file above the limit, before it sources any of them. The
default is Inf, which checks nothing.A code line is a physical line that is neither blank nor entirely a
comment. The R parser identifies the comments, so a #
inside a string does not hide a line. org::loc_per_file()
applies the same count on its own, if you want to see the numbers
without setting a limit.
Run.RRun.R is your main analysis script. It orchestrates the
entire workflow:
All code sections SHOULD be in functions in the R/
folder. You SHOULD NOT have multiple main files. Multiple main files
create confusion when you return to your code later. You MAY have
versioned files (e.g., Run_v01.R, Run_v02.R).
A later version supersedes an earlier one.
R/ directoryAll analysis functions SHOULD be defined in
org::project$home/R. initialize_project()
automatically sources every R script in
org::project$home/R.
The code below is a complete example of a project structure.
# Initialize the project
org::initialize_project(
env = .GlobalEnv,
home = "/git/analyses/2019/analysis3/",
results = "/dropbox/analyses_results/2019/analysis3/",
data_raw = "/data/analyses/2019/analysis3/"
)
# Document changes in archived results
txt <- glue::glue("
2019-01-01:
Included:
- Table 1
- Table 2
2019-02-02:
Changed Table 1 from mean -> median
", .trim=FALSE)
org::write_text(
txt = txt,
file = fs::path(org::project$results, "info.txt")
)
# Load required packages
library(data.table)
library(ggplot2)
# Run analysis
d <- clean_data() # Accesses data from org::project$data_raw
table_1(d) # Saves to org::project$results_today
figure_1(d) # Saves to org::project$results_today
figure_2(d) # Saves to org::project$results_todayA research article often needs multiple versions, such as the initial
submission and the resubmissions. org manages this with
date-based versioning.
Run.R to
Run_YYYY_MM_DD_submission_1.R.R/ to
R_YYYY_MM_DD_submission_1/.This preserves the code that produced the results for each submission. Every change is then deliberate and intentional.
Team members often have different folder structures. You can specify
several possible paths for one folder. org automatically
selects the first path that exists:
# Team member setup - org will use the first existing path
org::initialize_project(
env = .GlobalEnv,
home = c(
"/Users/teammate1/projects/analysis3/", # Mac user
"/home/teammate2/analysis3/", # Linux user
"C:/Users/teammate3/analysis3/" # Windows user
),
results = c(
"/Users/teammate1/Dropbox/results/",
"/home/teammate2/dropbox/results/",
"C:/Users/teammate3/Dropbox/results/"
),
data_raw = c(
"/Users/teammate1/data/analysis3/",
"/home/teammate2/data/analysis3/",
"C:/shared_drive/data/analysis3/"
)
)The same initialization code then works across different team members’ machines, with no changes.
Store your project components in appropriate locations.
# Code (GitHub)
git/
└── analyses/
├── 2018/
│ ├── analysis_1/ # org::project$home
│ │ ├── Run.R
│ │ └── R/
│ │ ├── clean_data.R
│ │ ├── descriptives.R
│ │ ├── analysis.R
│ │ └── figure_1.R
│ └── analysis_2/
└── 2019/
└── analysis_3/
# Results (Dropbox)
dropbox/
└── analyses_results/
├── 2018/
│ ├── analysis_1/ # org::project$results
│ │ ├── 2018-03-12/ # org::project$results_today
│ │ │ ├── table_1.xlsx
│ │ │ └── figure_1.png
│ │ ├── 2018-03-15/
│ │ └── 2018-03-18/
│ └── analysis_2/
└── 2019/
└── analysis_3/
# Data (Local)
data/
└── analyses/
├── 2018/
│ ├── analysis_1/ # org::project$data_raw
│ │ └── data.xlsx
│ └── analysis_2/
└── 2019/
└── analysis_3/
Use this structure for a project on a shared network drive, without GitHub or Dropbox.
project_name/ # org::project$home
├── Run.R
├── R/
│ ├── CleanData.R
│ ├── Descriptives.R
│ ├── Analysis1.R
│ └── Graphs1.R
├── paper/
│ └── paper.Rmd
├── results/ # org::project$results
│ └── 2018-03-12/ # org::project$results_today
│ ├── table1.xlsx
│ └── figure1.png
└── data_raw/ # org::project$data_raw
└── data.xlsx
Use this structure for a project with limited access.
project_name/ # org::project$home
├── Run.R
├── R/
│ ├── clean_data.R
│ ├── descriptives.R
│ ├── analysis.R
│ └── figure_1.R
├── results/ # org::project$results
│ └── 2018-03-12/ # org::project$results_today
│ ├── table_1.xlsx
│ └── figure_1.png
└── data_raw/ # org::project$data_raw
└── data.xlsx
Path components have standard names.
| Component | Name |
|---|---|
| /home/richard/test.src | Absolute (file)path |
| richard/test.src | Relative (file)path |
| /home/richard/ | Absolute (directory) path |
| ./richard/ | Relative (directory) path |
| richard | Directory |
| test.src | Filename |
A path specifies a location in a directory structure, while a filename only includes the file name itself. Directories only include directory name information.
The org package provides several key functions for
project management.
org::initialize_project(): Set up
project structure and source R files.org::set_results(): Change the results
folder after project initialization.org::project: Environment containing
all project folder locations.org::path(): Construct cross-platform
file paths.org::ls_files(): List files with
optional pattern matching.org::loc_per_file(): Count the code
lines in each of several R files.org::move_directory(): Move
directories safely.org::write_text(): Write text files
with consistent formatting.# 1. Initialize project structure
org::initialize_project(
env = .GlobalEnv,
home = "/path/to/your/analysis/",
results = "/path/to/results/",
data_raw = "/path/to/data/"
)
# 2. Create analysis functions in R/ folder
# 3. Run analysis from Run.R
# 4. Results automatically saved to org::project$results_todayRecommendation: you SHOULD always use
.GlobalEnv. It is much easier. All your functions
are then directly accessible, and you do not need to think about
environment scoping.
The org::path() function ensures your code works across
different operating systems.
# Cross-platform path construction
data_file <- org::path(org::project$data_raw, "survey_data.csv")
output_file <- org::path(org::project$results_today, "analysis_results.xlsx")
# Handles multiple path components
nested_path <- org::path("folder1", "subfolder", "file.txt")
# Removes double slashes automatically
clean_path <- org::path("folder//", "//file.txt") # Returns "folder/file.txt"org::path() for cross-platform
compatibility.help(package = "org")?org::initialize_project