diff --git a/DESCRIPTION b/DESCRIPTION index f11a912..353abc8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -9,8 +9,13 @@ Description: One paragraph description of what the package does as one or more full sentences. License: GPL (>= 2) Imports: - Rcpp + Rcpp, + graphics, + dplyr LinkingTo: Rcpp Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 Encoding: UTF-8 +Suggests: + testthat (>= 3.0.0) +Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 8f279de..aa6616c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,8 @@ # Generated by roxygen2: do not edit by hand +export(myplot) export(rcpp_hello_world) +import(dplyr) +import(graphics) importFrom(Rcpp,evalCpp) useDynLib(egpkg, .registration = TRUE) diff --git a/R/add_plot.R b/R/add_plot.R new file mode 100644 index 0000000..d8c949a --- /dev/null +++ b/R/add_plot.R @@ -0,0 +1,32 @@ +#' Add myplot function +#' @param x A numeric vector. +#' @param y A numeric vector. +#' @return A plot of x and y. +#' @examples +# Here is an example +#' set.seed(312) +#' x <- rnorm(100) +#' y <- rnorm(100) +#' myplot(x, y) +#' @import graphics +#' @import dplyr +#' @export +# Here is the function +myplot <- function(x, y) { + + if (!is.numeric(x) | !is.numeric(y)) { + stop("x and y must be numeric") + } + + plot(x, y, col = "blue", pch = 19, cex = 2) + + invisible( + list( + x = x, + y = y + ) + ) + +} + + diff --git a/R/ps_match.R b/R/ps_match.R new file mode 100644 index 0000000..e69de29 diff --git a/egpkg.Rproj.Rcheck/00check.log b/egpkg.Rproj.Rcheck/00check.log new file mode 100644 index 0000000..e710afe --- /dev/null +++ b/egpkg.Rproj.Rcheck/00check.log @@ -0,0 +1,8 @@ +* using log directory ‘/Users/u6046094/Desktop/Fall 24/PHS 7045 R Programming/Labs/R_package/egpkg-lab07/egpkg.Rproj.Rcheck’ +* using R version 4.4.1 (2024-06-14) +* using platform: aarch64-apple-darwin20 +* R was compiled by + Apple clang version 14.0.0 (clang-1400.0.29.202) + GNU Fortran (GCC) 12.2.0 +* running under: macOS Sonoma 14.6.1 +* using session charset: UTF-8 diff --git a/man/myplot.Rd b/man/myplot.Rd new file mode 100644 index 0000000..297dc3f --- /dev/null +++ b/man/myplot.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_plot.R +\name{myplot} +\alias{myplot} +\title{Add myplot function} +\usage{ +myplot(x, y) +} +\arguments{ +\item{x}{A numeric vector.} + +\item{y}{A numeric vector.} +} +\value{ +A plot of x and y. +} +\description{ +Add myplot function +} +\examples{ +set.seed(312) +x <- rnorm(100) +y <- rnorm(100) +myplot(x, y) +} diff --git a/src/ps_match.cpp b/src/ps_match.cpp new file mode 100644 index 0000000..aa8ac01 --- /dev/null +++ b/src/ps_match.cpp @@ -0,0 +1,183 @@ +#include + +using namespace Rcpp; + +// [[Rcpp::export]] +List ps_match1(const NumericVector & x) { + + int n = static_cast(x.size()); + + IntegerVector indices(n); + NumericVector values(n); + + for (int i = 0; i < n; ++i) { + + // Maximum value + double cur_best = std::numeric_limits< double >::max(); + int cur_i = 0; + + for (int j = 0; j < n; ++j) { + + // We can't compare to oneself + if (i == j) + continue; + + // If it is lower, then update + if (std::abs(x[i] - x[j]) < cur_best) { + + cur_best = std::abs(x[i] - x[j]); + cur_i = j; + + } + + } + + // In the end, we register the result + indices[i] = cur_i; + values[i] = x[cur_i]; + + } + + return List::create( + _["match_id"] = indices + 1, // We add one to match R's indices + _["match_x"] = values + ); + +} + +// [[Rcpp::export]] +List ps_match2(const NumericVector & x) { + + int n = static_cast(x.size()); + + IntegerVector indices(n); + NumericVector values(n); + + for (int i = 0; i < n; ++i) { + + // Instead of allocating new memory, we can point by reference + // (saves operations) + double & cur_best = values[i]; + int & cur_i = indices[i]; + + cur_best = std::numeric_limits< double >::max(); + + cur_i = 0; + + for (int j = 0; j < n; ++j) { + + // We can't compare to oneself + if (i == j) + continue; + + // If it is lower, then update + if (std::abs(x[i] - x[j]) < cur_best) { + + cur_best = std::abs(x[i] - x[j]); + cur_i = j; + + } + + } + + } + + for (int i = 0; i < n; ++i) + values[i] = x[indices[i]]; + + return List::create( + _["match_id"] = indices + 1, // We add one to match R's indices + _["match_x"] = values + ); + +} + +// [[Rcpp::export]] +List ps_match3(const NumericVector & x) { + + int n = static_cast(x.size()); + + IntegerVector indices(n); + NumericVector values(n); + values.fill(std::numeric_limits< double >::max()); + + for (int i = 0; i < n; ++i) { + + // Instead of allocating new memory, we can point by reference + // (saves operations) + double & cur_best = values[i]; + auto & cur_i = indices[i]; + + for (int j = 0; j < i; ++j) { + + // If it is lower, then update + double d = std::abs(x[i] - x[j]); + if (d < cur_best) { + + cur_best = d; + cur_i = j; + + } + + if (d < values[j]) { + + values[j] = d; + indices[j] = i; + + } + + } + + } + + for (int i = 0; i < n; ++i) + values[i] = x[indices[i]]; + + return List::create( + _["match_id"] = indices + 1, // We add one to match R's indices + _["match_x"] = values + ); + +} + + +/***R +set.seed(1231) +x <- cbind(runif(5)) + +ps_matchR <- function(x) { + + match_expected <- dist(x) |> as.matrix() + diag(match_expected) <- .Machine$integer.max + indices <- apply(match_expected, 1, which.min) + + list( + match_id = as.integer(unname(indices)), + match_x = x[indices] + ) + +} + +resR <- ps_matchR(x) +res1 <- ps_match1(x) +res2 <- ps_match2(x) +res3 <- ps_match3(x) + +cbind( + X = x, + R = resR$match_id, + Rcpp1 = res$match_id, + Rcpp2 = res2$match_id, + Rcpp3 = res3$match_id +) |> head() + +# Benchmarking +x <- cbind(runif(10000)) +bench::mark( + resR = ps_matchR(x)$match_x, + res1 = ps_match1(x)$match_x, + res2 = ps_match2(x)$match_x, + res3 = ps_match3(x)$match_x, + relative = TRUE +) +*/ diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 0000000..76adaf1 --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,12 @@ +# This file is part of the standard setup for testthat. +# It is recommended that you do not modify it. +# +# Where should you do additional test configuration? +# Learn more about the roles of various files in: +# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview +# * https://testthat.r-lib.org/articles/special-files.html + +library(testthat) +library(egpkg) + +test_check("egpkg") diff --git a/tests/testthat/test_add_plot.R b/tests/testthat/test_add_plot.R new file mode 100644 index 0000000..105b1ec --- /dev/null +++ b/tests/testthat/test_add_plot.R @@ -0,0 +1,9 @@ +# test add_plot function +# when input is not numeric + +library(testthat) +library(egpkg) + +test_that("add_plot function", { + expect_error(myplot("a", "b"), "x and y must be numeric") +})