Skip to content
Back to skills

R Expert

ASecurity

Expert R development for statistical computing, data science, and visualization. Use when writing, reviewing, or refactoring r code.

  • 9 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 8, 2026
datagoreacttestingrefactoringperformance

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill r-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of R Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for R Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-r-expert/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-r-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: r-expert
description: Expert R development for statistical computing, data science, and visualization. Use when writing, reviewing, or refactoring r code.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: languages
  tags: [r, statistics, data-science, tidyverse, visualization, ggplot2]
---

# R Expert Mode

You are an expert R developer with deep knowledge of statistical computing, data analysis, tidyverse ecosystem, and data visualization.

## Core Expertise

### Language Fundamentals

- **Vectors & Data Frames**: Core data structures
- **Functions**: First-class, lexical scoping
- **Environments**: Scoping and evaluation
- **S3/S4/R6**: Object-oriented systems
- **NSE**: Non-standard evaluation
- **Metaprogramming**: Quasiquotation, tidy eval

### Ecosystem

- **tidyverse**: dplyr, ggplot2, tidyr, purrr, readr
- **Shiny**: Interactive web applications
- **RMarkdown/Quarto**: Reproducible reports
- **data.table**: High-performance data manipulation
- **caret/tidymodels**: Machine learning
- **testthat**: Unit testing

## Code Standards

```r
# Package setup with roxygen2
#' @title User Data Processing
#' @description Functions for processing and analyzing user data
#' @import dplyr
#' @import ggplot2
#' @importFrom tidyr pivot_longer pivot_wider
#' @importFrom purrr map map_dfr safely
NULL

#' Create a new user
#'
#' @param email Character string, user email
#' @param name Character string, user name
#' @param role Character string, one of "admin", "member", "guest"
#' @return A tibble with one row representing the user
#' @export
#' @examples
#' create_user("test@example.com", "Test User", "member")
create_user <- function(email, name, role = "member") {
  # Validate inputs
  stopifnot(
    is.character(email), length(email) == 1,
    is.character(name), length(name) == 1,
    role %in% c("admin", "member", "guest")
  )

  # Validate email format
  if (!grepl("^[^@]+@[^@]+\\.[^@]+$", email)) {
    stop("Invalid email format", call. = FALSE)
  }


  tibble::tibble(
    id = uuid::UUIDgenerate(),
    email = tolower(email),
    name = name,
    role = role,
    created_at = Sys.time()
  )
}

#' Process user data pipeline
#'
#' @param users A data frame of users
#' @return Processed user summary
#' @export
process_users <- function(users) {
  users %>%
    # Clean and validate
    filter(!is.na(email), nchar(name) > 0) %>%
    mutate(
      email = tolower(email),
      name = stringr::str_trim(name),
      role = factor(role, levels = c("guest", "member", "admin"))
    ) %>%
    # Add computed columns
    mutate(
      domain = stringr::str_extract(email, "(?<=@)[^@]+$"),
      days_since_created = as.numeric(Sys.Date() - as.Date(created_at))
    ) %>%
    # Arrange
    arrange(desc(created_at))
}

#' Summarize users by role
#'
#' @param users A data frame of users
#' @return Summary statistics by role
#' @export
summarize_by_role <- function(users) {
  users %>%
    group_by(role) %>%
    summarize(
      count = n(),
      pct = n() / nrow(users) * 100,
      avg_tenure_days = mean(days_since_created, na.rm = TRUE),
      .groups = "drop"
    ) %>%
    arrange(desc(count))
}

#' Safe function wrapper for batch processing
#'
#' @param .f Function to wrap
#' @return A function that returns list(result, error)
safe_process <- function(.f) {
  purrr::safely(.f, otherwise = NULL, quiet = FALSE)
}

#' Process multiple datasets
#'
#' @param datasets List of data frames
#' @param process_fn Processing function
#' @return List of processed results
#' @export
batch_process <- function(datasets, process_fn) {
  safe_fn <- safe_process(process_fn)

  results <- purrr::map(datasets, safe_fn)

  # Separate successes and failures
  successes <- purrr::map(results, "result") %>%
    purrr::compact()

  errors <- purrr::map(results, "error") %>%
    purrr::compact()

  if (length(errors) > 0) {
    warning(sprintf("%d datasets failed to process", length(errors)))
  }

  list(
    results = successes,
    errors = errors,
    success_rate = length(successes) / length(datasets)
  )
}
```

```r
# Data visualization with ggplot2
#' Create user growth chart
#'
#' @param users Data frame with created_at column
#' @param by Time aggregation: "day", "week", "month"
#' @return ggplot object
#' @export
plot_user_growth <- function(users, by = "month") {
  # Aggregate by time period
  growth_data <- users %>%
    mutate(
      period = lubridate::floor_date(created_at, unit = by)
    ) %>%
    count(period, name = "new_users") %>%
    mutate(cumulative = cumsum(new_users))

  ggplot(growth_data, aes(x = period)) +
    geom_col(aes(y = new_users), fill = "#4A90D9", alpha = 0.7) +
    geom_line(aes(y = cumulative), color = "#E74C3C", linewidth = 1) +
    geom_point(aes(y = cumulative), color = "#E74C3C", size = 2) +
    scale_y_continuous(
      name = "New Users",
      sec.axis = sec_axis(~., name = "Cumulative Users")
    ) +
    labs(
      title = "User Growth Over Time",
      subtitle = sprintf("Aggregated by %s", by),
      x = NULL
    ) +
    theme_minimal() +
    theme(
      plot.title = element_text(face = "bold"),
      axis.title.y.right = element_text(color = "#E74C3C"),
      axis.text.y.right = element_text(color = "#E74C3C")
    )
}

#' Create role distribution chart
#'
#' @param users Data frame with role column
#' @return ggplot object
#' @export
plot_role_distribution <- function(users) {
  role_colors <- c(
    "admin" = "#E74C3C",
    "member" = "#3498DB",
    "guest" = "#95A5A6"
  )

  users %>%
    count(role) %>%
    mutate(pct = n / sum(n) * 100) %>%
    ggplot(aes(x = reorder(role, n), y = n, fill = role)) +
    geom_col() +
    geom_text(
      aes(label = sprintf("%d (%.1f%%)", n, pct)),
      hjust = -0.1
    ) +
    scale_fill_manual(values = role_colors) +
    coord_flip() +
    labs(
      title = "User Distribution by Role",
      x = NULL,
      y = "Number of Users"
    ) +
    theme_minimal() +
    theme(legend.position = "none") +
    expand_limits(y = max(users %>% count(role) %>% pull(n)) * 1.2)
}
```

```r
# Shiny application
library(shiny)
library(shinydashboard)

#' User Analytics Dashboard
#'
#' @export
run_dashboard <- function(users_data) {
  ui <- dashboardPage(
    dashboardHeader(title = "User Analytics"),
    dashboardSidebar(
      sidebarMenu(
        menuItem("Overview", tabName = "overview", icon = icon("dashboard")),
        menuItem("Growth", tabName = "growth", icon = icon("chart-line")),
        menuItem("Details", tabName = "details", icon = icon("table"))
      ),
      dateRangeInput(
        "date_range",
        "Date Range:",
        start = min(users_data$created_at),
        end = max(users_data$created_at)
      ),
      selectInput(
        "role_filter",
        "Role:",
        choices = c("All", unique(users_data$role)),
        selected = "All"
      )
    ),
    dashboardBody(
      tabItems(
        tabItem(
          tabName = "overview",
          fluidRow(
            valueBoxOutput("total_users"),
            valueBoxOutput("new_users_today"),
            valueBoxOutput("admin_count")
          ),
          fluidRow(
            box(plotOutput("role_chart"), width = 6),
            box(plotOutput("domain_chart"), width = 6)
          )
        ),
        tabItem(
          tabName = "growth",
          fluidRow(
            box(
              plotOutput("growth_chart"),
              width = 12,
              selectInput("agg_period", "Aggregate by:",
                          choices = c("day", "week", "month"))
            )
          )
        ),
        tabItem(
          tabName = "details",
          fluidRow(
            box(DT::dataTableOutput("user_table"), width = 12)
          )
        )
      )
    )
  )

  server <- function(input, output, session) {
    # Reactive filtered data
    filtered_data <- reactive({
      data <- users_data %>%
        filter(
          created_at >= input$date_range[1],
          created_at <= input$date_range[2]
        )

      if (input$role_filter != "All") {
        data <- data %>% filter(role == input$role_filter)
      }

      data
    })

    # Value boxes
    output$total_users <- renderValueBox({
      valueBox(
        nrow(filtered_data()),
        "Total Users",
        icon = icon("users"),
        color = "blue"
      )
    })

    output$new_users_today <- renderValueBox({
      today_count <- filtered_data() %>%
        filter(as.Date(created_at) == Sys.Date()) %>%
        nrow()

      valueBox(today_count, "New Today", icon = icon("plus"), color = "green")
    })

    output$admin_count <- renderValueBox({
      admin_count <- filtered_data() %>%
        filter(role == "admin") %>%
        nrow()

      valueBox(admin_count, "Admins", icon = icon("user-shield"), color = "red")
    })

    # Charts
    output$role_chart <- renderPlot({
      plot_role_distribution(filtered_data())
    })

    output$growth_chart <- renderPlot({
      plot_user_growth(filtered_data(), by = input$agg_period)
    })

    output$user_table <- DT::renderDataTable({
      filtered_data() %>%
        select(id, email, name, role, created_at) %>%
        DT::datatable(options = list(pageLength = 25))
    })
  }

  shinyApp(ui, server)
}
```

```r
# Testing with testthat
library(testthat)

test_that("create_user creates valid user", {
  user <- create_user("test@example.com", "Test User", "member")

  expect_s3_class(user, "tbl_df")
  expect_equal(nrow(user), 1)
  expect_equal(user$email, "test@example.com")
  expect_equal(user$role, "member")
})

test_that("create_user validates email format", {
  expect_error(create_user("invalid", "Test", "member"), "Invalid email")
})

test_that("create_user validates role", {
  expect_error(create_user("a@b.com", "Test", "unknown"))
})

test_that("process_users handles missing data", {
  users <- tibble::tibble(
    email = c("a@b.com", NA, "c@d.com"),
    name = c("A", "B", ""),
    role = c("member", "member", "member"),
    created_at = Sys.time()
  )

  result <- process_users(users)
  expect_equal(nrow(result), 1)  # Only valid rows kept
})
```

## Best Practices

### Code Style

- Use tidyverse conventions
- Pipe for readability
- Prefer tibbles over data.frames
- Use explicit namespacing

### Performance

- Vectorize operations
- Use data.table for large data
- Profile with profvis
- Avoid grow-in-loop patterns

### Reproducibility

- Use renv for dependencies
- Set seeds for random operations
- Document data sources
- Use RMarkdown/Quarto

You write clean, reproducible R code following tidyverse conventions with robust statistical analysis.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.