├── reach ├── LICENSE ├── tests │ ├── testthat.R │ └── testthat │ │ ├── matlab_test_fct_1.m │ │ ├── test_all_examples.R │ │ ├── matlab_test_fct_2.m │ │ ├── matlab_test_fct_3.m │ │ ├── test_runMatlabCommand.R │ │ ├── test_isvector.R │ │ ├── test_runMatlabScript.R │ │ ├── test_matWrite.R │ │ ├── test_convert2RData.R │ │ └── test_runMatlabFct.R ├── .Rbuildignore ├── NAMESPACE ├── man │ ├── getMatlabCall.Rd │ ├── isWin.Rd │ ├── getMacMatlab.Rd │ ├── str_extractCommaSepArgs.Rd │ ├── isvector.Rd │ ├── rList2Cell.Rd │ ├── matWrite.Rd │ ├── runMatlabScript.Rd │ ├── matlabExportList.Rd │ ├── runMatlabCommand.Rd │ ├── convert2RData.Rd │ └── runMatlabFct.Rd ├── R │ ├── isWin.R │ ├── getMatlabCall.R │ ├── getMacMatlab.R │ ├── str_extractCommaSepArgs.R │ ├── isvector.R │ ├── runMatlabScript.R │ ├── matWrite.R │ ├── runMatlabCommand.R │ ├── matlabExportList.R │ ├── convert2RData.R │ └── runMatlabFct.R ├── DESCRIPTION └── inst │ └── matlab │ └── rList2Cell.m ├── reach.pdf ├── surf.png ├── reach_0.4.5.tar.gz ├── .travis.yml ├── .gitignore ├── rList2Cell.m ├── NEWS.md └── README.md /reach/LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2016 2 | COPYRIGHT HOLDER: Christoph Schmidt 3 | -------------------------------------------------------------------------------- /reach.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schmidtchristoph/reach/HEAD/reach.pdf -------------------------------------------------------------------------------- /surf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schmidtchristoph/reach/HEAD/surf.png -------------------------------------------------------------------------------- /reach/tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(reach) 3 | 4 | test_check("reach") 5 | -------------------------------------------------------------------------------- /reach_0.4.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schmidtchristoph/reach/HEAD/reach_0.4.5.tar.gz -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | sudo: required 3 | warnings_are_errors: true 4 | before_install: 5 | - cd ./reach 6 | -------------------------------------------------------------------------------- /reach/.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^packrat$ 4 | ^\.Rprofile$ 5 | ^\.travis\.yml$ 6 | ^tests/$ 7 | -------------------------------------------------------------------------------- /reach/tests/testthat/matlab_test_fct_1.m: -------------------------------------------------------------------------------- 1 | function [ out ] = matlab_test_fct_1( varargin ) 2 | 3 | out = varargin; 4 | 5 | end 6 | -------------------------------------------------------------------------------- /reach/tests/testthat/test_all_examples.R: -------------------------------------------------------------------------------- 1 | library(reach) 2 | library(testthat) 3 | 4 | context("Testing examples in documentation") 5 | 6 | test_examples() 7 | -------------------------------------------------------------------------------- /reach/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(convert2RData) 4 | export(matWrite) 5 | export(matlabExportList) 6 | export(runMatlabCommand) 7 | export(runMatlabFct) 8 | export(runMatlabScript) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | reach/.Rproj.user 2 | *.Rproj 3 | .Rhistory 4 | *.RData 5 | *.mat 6 | reach/packrat/ 7 | reach/.Rprofile 8 | *.pdf 9 | reach/.gitignore 10 | .Rproj.user 11 | reach/man/.Rapp.history 12 | TODO.md 13 | preparePush -------------------------------------------------------------------------------- /reach/tests/testthat/matlab_test_fct_2.m: -------------------------------------------------------------------------------- 1 | function [varargout] = matlab_test_fct_2(in) 2 | nr_out = nargout; 3 | 4 | varargout = cell(1, nr_out); 5 | 6 | 7 | for(k=1:nr_out) 8 | varargout{k} = k*in; 9 | end 10 | 11 | 12 | 13 | end -------------------------------------------------------------------------------- /reach/tests/testthat/matlab_test_fct_3.m: -------------------------------------------------------------------------------- 1 | function [ varargout ] = matlab_test_fct_3( varargin ) 2 | nr_out = nargout; 3 | nr_in = nargin; 4 | 5 | for(k = 1:nr_out) 6 | try 7 | varargout{k} = varargin{k}*5; 8 | catch me 9 | varargout{k} = 99; 10 | end 11 | end 12 | 13 | 14 | end -------------------------------------------------------------------------------- /reach/man/getMatlabCall.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getMatlabCall.R 3 | \name{getMatlabCall} 4 | \alias{getMatlabCall} 5 | \title{Get OS-specific Matlab program call string} 6 | \usage{ 7 | getMatlabCall() 8 | } 9 | \description{ 10 | Returns the matlab programm name for OS X, Linux and Windows. 11 | } 12 | \author{ 13 | Christoph Schmidt 14 | } 15 | 16 | -------------------------------------------------------------------------------- /reach/man/isWin.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/isWin.R 3 | \name{isWin} 4 | \alias{isWin} 5 | \title{Is the underlying OS Windows?} 6 | \usage{ 7 | isWin() 8 | } 9 | \description{ 10 | Returns a logical that indicates whether \pkg{reach} is used on a Windows 11 | machine (which is useful information for adjusting path specifications). 12 | } 13 | \author{ 14 | Christoph Schmidt 15 | } 16 | 17 | -------------------------------------------------------------------------------- /reach/R/isWin.R: -------------------------------------------------------------------------------- 1 | #' Is the underlying OS Windows? 2 | #' 3 | #' Returns a logical that indicates whether \pkg{reach} is used on a Windows 4 | #' machine (which is useful information for adjusting path specifications). 5 | #' 6 | #' @author Christoph Schmidt 7 | 8 | # 03.06.16 9 | isWin <- function(){ 10 | si <- Sys.info() 11 | osName <- si[["sysname"]] 12 | 13 | if(osName=="Windows"){ 14 | return(TRUE) 15 | 16 | } else { 17 | return(FALSE) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reach/man/getMacMatlab.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/getMacMatlab.R 3 | \name{getMacMatlab} 4 | \alias{getMacMatlab} 5 | \title{Returns the name of the Matlab app of the latest version in the OSX 6 | Applications folder} 7 | \usage{ 8 | getMacMatlab() 9 | } 10 | \value{ 11 | Latest Matlab application on Mac OSX 12 | } 13 | \description{ 14 | Utility function that returns the latest Matlab app (with the highest version 15 | number) in the OSX Applications folder, e.g. "MATLAB_R2014a.app". 16 | } 17 | \author{ 18 | Christoph Schmidt 19 | } 20 | 21 | -------------------------------------------------------------------------------- /reach/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: reach 2 | Title: Improving interoperability between R and MATLAB 3 | Version: 0.4.5 4 | Author: Christoph Schmidt 5 | Maintainer: Christoph Schmidt 6 | Description: An implementation of utility functions for improving R < > MATLAB 7 | interoperability. 8 | SystemRequirements: Matlab 9 | Depends: 10 | R (>= 3.2.1) 11 | Imports: 12 | stringr (>= 1.0.0), 13 | R.matlab (>= 3.2.0) 14 | Suggests: 15 | testthat (>= 0.10.0) 16 | LazyData: true 17 | License: MIT + file LICENSE 18 | URL: https://github.com/schmidtchristoph/reach 19 | BugReports: https://github.com/schmidtchristoph/reach/issues 20 | RoxygenNote: 5.0.1 21 | -------------------------------------------------------------------------------- /reach/R/getMatlabCall.R: -------------------------------------------------------------------------------- 1 | #' Get OS-specific Matlab program call string 2 | #' 3 | #' Returns the matlab programm name for OS X, Linux and Windows. 4 | #' 5 | #' @author Christoph Schmidt 6 | 7 | # 03.06.16 8 | 9 | getMatlabCall <- function(){ 10 | si <- Sys.info() 11 | osName <- si[["sysname"]] 12 | 13 | if(osName=="Darwin"){ # OS X 14 | matl <- getMacMatlab() 15 | matlabCall <- paste("/Applications/", matl, "/bin/matlab", sep="") 16 | 17 | } else if(osName== "Linux" || osName=="Windows"){ # (-: || )-: 18 | matlabCall <- "matlab" 19 | 20 | 21 | } else { 22 | warning("reach::getMatlabCall: Unsupported operating system.") 23 | } 24 | 25 | return(matlabCall) 26 | } 27 | -------------------------------------------------------------------------------- /reach/R/getMacMatlab.R: -------------------------------------------------------------------------------- 1 | #' Returns the name of the Matlab app of the latest version in the OSX 2 | #' Applications folder 3 | #' 4 | #' Utility function that returns the latest Matlab app (with the highest version 5 | #' number) in the OSX Applications folder, e.g. "MATLAB_R2014a.app". 6 | #' 7 | #' @return Latest Matlab application on Mac OSX 8 | #' 9 | #' @author Christoph Schmidt 10 | 11 | # 13.04.15 12 | 13 | getMacMatlab <- function(){ 14 | fil <- list.files("/Applications/") 15 | loc <- stringr::str_locate(fil, "MATLAB") 16 | ind <- which(!is.na(loc[,1])) 17 | 18 | matl <- fil[tail(ind,1)] # select Matlab corresponding to the last entry in 'ind' (should be highest Matlab version number) 19 | return(matl) 20 | } 21 | -------------------------------------------------------------------------------- /reach/man/str_extractCommaSepArgs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/str_extractCommaSepArgs.R 3 | \name{str_extractCommaSepArgs} 4 | \alias{str_extractCommaSepArgs} 5 | \title{Extract arguments given in a string, separated by commas} 6 | \usage{ 7 | str_extractCommaSepArgs(args_str) 8 | } 9 | \arguments{ 10 | \item{args_str}{character vector containing (variable) names separated by 11 | commas} 12 | } 13 | \value{ 14 | Returns a list containing the (variable) names (characters) stored in 15 | \code{args_str}. 16 | } 17 | \description{ 18 | Parses an input string and returns the comma separated arguments. 19 | } 20 | \examples{ 21 | vars <- "A, B, myChar, level, k, test, te_st , newval" 22 | reach:::str_extractCommaSepArgs(vars) 23 | 24 | } 25 | \author{ 26 | Christoph Schmidt 27 | } 28 | 29 | -------------------------------------------------------------------------------- /reach/man/isvector.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/isvector.R 3 | \name{isvector} 4 | \alias{isvector} 5 | \title{Checks whether input variable is a vector} 6 | \usage{ 7 | isvector(obj) 8 | } 9 | \arguments{ 10 | \item{obj}{Data whose type is being tested to be a vector (n,1) or (1,n), n>1} 11 | } 12 | \value{ 13 | This function returns a boolean indicating whether input obj is a 14 | vector 15 | } 16 | \description{ 17 | An input is considered to be a vector if it has dimensions (n,1) or (1,n), 18 | n>1. Returns TRUE if the input is a vector according to this definition. 19 | Therefore, input that is a one-dimensional 'matrix' in R (is.matrix = TRUE 20 | and is.vector = FALSE) would also be regarded as a vector. 21 | } 22 | \examples{ 23 | v <- c(1, 2, 3) 24 | reach:::isvector(v) 25 | 26 | reach:::isvector(t(v)) 27 | 28 | m <- matrix(1:4, 2, 2) 29 | reach:::isvector(m) 30 | 31 | v <- matrix(1:4, 4, 1) 32 | reach:::isvector(v) 33 | 34 | } 35 | \author{ 36 | Christoph Schmidt 37 | } 38 | 39 | -------------------------------------------------------------------------------- /reach/man/rList2Cell.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/matlabExportList.R 3 | \name{rList2Cell} 4 | \alias{rList2Cell} 5 | \title{Conversion of a R list, which was processed with the R function 6 | "matlabExportList()" and then imported to Matlab to a Matlab cell-array} 7 | \usage{ 8 | rList2Cell(importlist) 9 | } 10 | \arguments{ 11 | \item{importlist}{the imported struct equivalent of the R list, which was 12 | reformated in R using matlabExportList.R and exported to Matlab using 13 | writeMat() from the R.matlab package} 14 | } 15 | \value{ 16 | A Matlab cell-array containing in each cell the corresponding element 17 | of the original R list data (before it was reformated using 18 | matlabExportList.R()); also multi-arrays stored in the original R list 19 | datatype are recovered 20 | } 21 | \description{ 22 | This is a Matlab function. It transforms a R list datatype (which is imported 23 | in Matlab as a struct) to a Matlab cell-array. Also recovers/ reformats 24 | multi-arrays contained in this R list (which are only exported as vectors). 25 | } 26 | \author{ 27 | Christoph Schmidt 28 | } 29 | \seealso{ 30 | \code{\link{matlabExportList}} 31 | } 32 | 33 | -------------------------------------------------------------------------------- /reach/R/str_extractCommaSepArgs.R: -------------------------------------------------------------------------------- 1 | #' Extract arguments given in a string, separated by commas 2 | #' 3 | #' Parses an input string and returns the comma separated arguments. 4 | #' 5 | #' @param args_str character vector containing (variable) names separated by 6 | #' commas 7 | #' 8 | #' @return Returns a list containing the (variable) names (characters) stored in 9 | #' \code{args_str}. 10 | #' 11 | #' @examples 12 | #' vars <- "A, B, myChar, level, k, test, te_st , newval" 13 | #' reach:::str_extractCommaSepArgs(vars) 14 | #' 15 | #' @author Christoph Schmidt 16 | 17 | # 11.12.15 18 | 19 | str_extractCommaSepArgs <- function(args_str){ 20 | inp <- list() 21 | ind_start <- 1 22 | k <- 1 23 | bool <- TRUE 24 | 25 | 26 | while(bool){ 27 | args_str <- stringr::str_sub( args_str, ind_start ) 28 | ind_end <- stringr::str_locate( args_str, "," )[1,1] 29 | 30 | if(is.na(ind_end)){ 31 | bool <- FALSE 32 | ind_end <- stringr::str_length(args_str) + 1 33 | } 34 | 35 | this_arg <- stringr::str_sub( args_str, 1, ind_end-1 ) 36 | inp[[k]] <- stringr::str_trim(this_arg) 37 | ind_start <- ind_end + 1 38 | k <- k + 1 39 | } 40 | 41 | return(inp) 42 | } 43 | -------------------------------------------------------------------------------- /reach/tests/testthat/test_runMatlabCommand.R: -------------------------------------------------------------------------------- 1 | library(reach) 2 | library(testthat) 3 | 4 | context("Running Matlab commands from the R shell") 5 | 6 | test_that("runMatlabCommand terminates", { 7 | testthat::skip_on_travis() # since Travis CI has no MATLAB installed tests are going to fail there 8 | 9 | 10 | 11 | commandName <- "x=1:2:7; y=3; disp(x); disp(x.^y); quit" 12 | expect_that(runMatlabCommand(commandName), not(throws_error())) 13 | 14 | 15 | commandName2 <- "M=magic(4); disp(M); eig(M)" 16 | expect_that(runMatlabCommand(commandName2), not(throws_error())) 17 | 18 | 19 | commandName3 <- "M=magic(4); disp(M); eig(M)," 20 | expect_that(runMatlabCommand(commandName3), not(throws_error())) 21 | 22 | 23 | commandName4 <- "M=magic(4); disp(M); eig(M);" 24 | expect_that(runMatlabCommand(commandName4), not(throws_error())) 25 | 26 | 27 | wrong_but_corrected_commandName <- "M=magic(4); disp(M); eig(M) quit" 28 | expect_that(runMatlabCommand(wrong_but_corrected_commandName), not(throws_error())) 29 | 30 | 31 | commandName3 <- "A=magic(3); save('magic.mat', 'A', '-v7'); quit" 32 | expect_that(runMatlabCommand(commandName3), not(throws_error())) 33 | input <- R.matlab::readMat("magic.mat") 34 | print(input$A) 35 | A <- structure(c(8, 3, 4, 1, 5, 9, 6, 7, 2), .Dim = c(3L, 3L)) 36 | expect_equal(A, input$A) 37 | invisible(capture.output(file.remove("magic.mat"))) 38 | }) 39 | -------------------------------------------------------------------------------- /reach/R/isvector.R: -------------------------------------------------------------------------------- 1 | #' Checks whether input variable is a vector 2 | #' 3 | #' An input is considered to be a vector if it has dimensions (n,1) or (1,n), 4 | #' n>1. Returns TRUE if the input is a vector according to this definition. 5 | #' Therefore, input that is a one-dimensional 'matrix' in R (is.matrix = TRUE 6 | #' and is.vector = FALSE) would also be regarded as a vector. 7 | #' 8 | #' @param obj Data whose type is being tested to be a vector (n,1) or (1,n), n>1 9 | #' 10 | #' @return This function returns a boolean indicating whether input obj is a 11 | #' vector 12 | #' 13 | #' @examples 14 | #' v <- c(1, 2, 3) 15 | #' reach:::isvector(v) 16 | #' 17 | #' reach:::isvector(t(v)) 18 | #' 19 | #' m <- matrix(1:4, 2, 2) 20 | #' reach:::isvector(m) 21 | #' 22 | #' v <- matrix(1:4, 4, 1) 23 | #' reach:::isvector(v) 24 | #' 25 | #' @author Christoph Schmidt 26 | 27 | # 14.09.15 28 | 29 | # This function is merely a utility function and is part of the una package 30 | # (utility functions for network analysis). 31 | # It is distributed within reach for ease of use. 32 | 33 | isvector <- function(obj){ 34 | if(length(obj)<=1) { # scalar 35 | return(FALSE) 36 | } 37 | 38 | 39 | 40 | bool <- FALSE 41 | 42 | 43 | if(is.vector(obj)) { 44 | bool <- TRUE 45 | 46 | } else if(is.matrix(obj) & length(dim(obj))==2 & min(dim(obj))==1) { 47 | bool <- TRUE 48 | 49 | } else if(is.array(obj) & length(dim(obj))==1) { 50 | bool <- TRUE 51 | } 52 | 53 | return(bool) 54 | } 55 | 56 | -------------------------------------------------------------------------------- /reach/tests/testthat/test_isvector.R: -------------------------------------------------------------------------------- 1 | library(reach) 2 | library(testthat) 3 | 4 | context("isvector") 5 | 6 | 7 | 8 | test_that("a scalar value is not a vector and does correctly return FALSE", { 9 | s <- 122 10 | expect_identical(reach:::isvector(s), FALSE) 11 | 12 | 13 | s <- "here" 14 | expect_identical(reach:::isvector(s), FALSE) 15 | 16 | 17 | s <- FALSE 18 | expect_identical(reach:::isvector(s), FALSE) 19 | 20 | 21 | s <- TRUE 22 | expect_identical(reach:::isvector(s), FALSE) 23 | }) 24 | 25 | 26 | 27 | 28 | 29 | test_that("a vector of different type correctly yields TRUE", { 30 | v <- c(1, 0.3, 0.0007, 45) 31 | expect_identical(reach:::isvector(v), TRUE) 32 | expect_identical(reach:::isvector( t(v) ), TRUE) 33 | 34 | 35 | v <- c("here", "and", "beer") 36 | expect_identical(reach:::isvector(v), TRUE) 37 | expect_identical(reach:::isvector( t(v) ), TRUE) 38 | 39 | 40 | v <- c(TRUE, FALSE, FALSE) 41 | expect_identical(reach:::isvector(v), TRUE) 42 | expect_identical(reach:::isvector( t(v) ), TRUE) 43 | 44 | 45 | v <- matrix(c(1, 2, 3, 4, 5, 6), 6, 1) 46 | expect_identical(reach:::isvector(v), TRUE) 47 | expect_identical(reach:::isvector( t(v) ), TRUE) 48 | 49 | 50 | v <- matrix(c(77, 2, 33, 444, 5, 0), 1, 6) 51 | expect_identical(reach:::isvector(v), TRUE) 52 | expect_identical(reach:::isvector( t(v) ), TRUE) 53 | }) 54 | 55 | 56 | 57 | 58 | 59 | test_that("a matrix correctly yields FALSE", { 60 | m <- matrix(c(1, 2, 3, 4), 2, 2) 61 | expect_identical(reach:::isvector(m), FALSE) 62 | expect_identical(reach:::isvector( t(m) ), FALSE) 63 | 64 | 65 | m <- array(1:3, c(2,4)) 66 | expect_identical(reach:::isvector(m), FALSE) 67 | expect_identical(reach:::isvector( t(m) ), FALSE) 68 | }) 69 | -------------------------------------------------------------------------------- /reach/man/matWrite.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/matWrite.R 3 | \name{matWrite} 4 | \alias{matWrite} 5 | \title{Writes .mat files for exporting data to be used with Matlab} 6 | \usage{ 7 | matWrite(fn, vars) 8 | } 9 | \arguments{ 10 | \item{fn}{file name, a character string} 11 | 12 | \item{vars}{character vector containing a comma separated listing of 13 | variables to be saved in the matfile. The variables have to exist in the 14 | environment where the function was called from.} 15 | } 16 | \description{ 17 | Writes .mat files to store R session data using the R.matlab package and 18 | takes care that logicals and atomic vectors are saved properly: currently, 19 | R.matlab does not write logicals and atomic vectors (not 1D arrays/ matrices) 20 | in a way that they can be opened properly in Matlab (logicals will not be 21 | stored and atomic vectors will be transposed in the Matlab session - but they 22 | appear untransposed when read back from the .mat file into R using 23 | R.matlab::readMat()). This function is a convenient wrapper for 24 | R.matlab::writeMat() that stores logicals as 0 / 1 and that transposes atomic 25 | vectors when saving the matfile. 26 | } 27 | \examples{ 28 | A <- matrix(c(2,3,4,2, 1,1,2,6, 8,3,9,7), 3, 4, byrow = TRUE) 29 | b <- c(3, 5, 1, 9, 18, 2) # atomic vector 1x6 30 | cc <- array(b, c(1, length(b))) # array vector 1x6 31 | dd <- t(cc) # array vector 6x1 32 | myChar <- c("from", "R", "to", "Matlab") 33 | bool <- TRUE # logical 34 | k <- FALSE 35 | l <- FALSE 36 | fn <- "mytestmat" 37 | vars <- "A, b, cc, dd, myChar, bool, k, l" 38 | matWrite(fn, vars) 39 | unlink(paste(fn, ".mat", sep = "")) 40 | 41 | } 42 | \author{ 43 | Christoph Schmidt 44 | } 45 | 46 | -------------------------------------------------------------------------------- /reach/tests/testthat/test_runMatlabScript.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | 3 | 4 | context("runMatlabScript") 5 | 6 | 7 | 8 | 9 | test_that("throws an error if input script is not present in working directory before Matlab does", { 10 | testthat::skip_on_travis() 11 | 12 | expect_error(runMatlabScript("MYSCRIPT2.m"), "Input Matlab script does not exist in current working directory.") 13 | 14 | 15 | expect_error(runMatlabScript("noscript.m"), "Input Matlab script does not exist in current working directory.") 16 | 17 | 18 | expect_error(runMatlabScript("noscript"), "Input Matlab script does not exist in current working directory.") 19 | 20 | 21 | scriptName <- "myscript.m" 22 | scriptCode <- "x=1:2:7; y=3; z=x.^y; save xyz.mat x y z -v7" 23 | writeLines(scriptCode, con=scriptName) 24 | expect_that(runMatlabScript("myscript"), not(throws_error())) 25 | expect_that(runMatlabScript("myscript.m"), not(throws_error())) 26 | 27 | 28 | file.remove("myscript.m") 29 | file.remove("xyz.mat") 30 | }) 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | test_that("calling Matlab yields correct results", { 39 | testthat::skip_on_travis() 40 | 41 | scriptName <- "myscript.m" 42 | scriptCode <- "x=1:2:7; y=3; z=x.^y; M = magic(4); M3 = M^3; save TesT.mat x y z M M3 -v7" 43 | writeLines(scriptCode, con=scriptName) 44 | expect_that(runMatlabScript("myscript.m"), not(throws_error())) 45 | inp <- R.matlab::readMat("TesT.mat") 46 | 47 | x <- seq(1, 7, 2) 48 | y <- 3 49 | z <- x^y 50 | M <- structure(c(16, 5, 9, 4, 2, 11, 7, 14, 3, 10, 6, 15, 13, 8, 12, 1), .Dim = c(4L, 4L)) # M <- dput(matlab::magic(4)) 51 | M3 <- M %*% M %*% M 52 | 53 | expect_equal(x, as.vector(inp$x)) 54 | expect_equal(y, as.vector(inp$y)) 55 | expect_equal(z, as.vector(inp$z)) 56 | expect_equal(M, inp$M) 57 | expect_equal(M3, inp$M3) 58 | 59 | file.remove("myscript.m") 60 | file.remove("TesT.mat") 61 | }) 62 | -------------------------------------------------------------------------------- /rList2Cell.m: -------------------------------------------------------------------------------- 1 | function [ C ] = rList2Cell( importlist ) 2 | % Transforms a R list datatype (which is imported as a struct) to 3 | % a Matlab cell-array. Also recovers/ reformats multi-arrays contained 4 | % in this R list (which are only exported as vectors). 5 | % 6 | % importlist the imported struct equivalent of the R list, 7 | % which was reformated in R using 8 | % matlabExportList.R and exported to Matlab using 9 | % writeMat() from the R.matlab package 10 | % 11 | % 12 | % 13 | % 14 | % 15 | % 16 | % 17 | % C a Matlab cell-array containing in each cell the 18 | % corresponding element of the original R list data 19 | % (before it was reformated using 20 | % matlabExportList.R()); also multi-arrays stored in 21 | % the original R list datatype are recovered 22 | % 23 | % USAGE: 24 | % 25 | % C = rList2Cell(importlist) 26 | % 27 | % 28 | % See also: matlabExportList.R 29 | % 30 | % The MIT License (MIT) (http://opensource.org/licenses/MIT) 31 | % Copyright (c) 2015 Christoph Schmidt 32 | 33 | % 21.11.14 34 | 35 | C = struct2cell(importlist); 36 | 37 | 38 | 39 | 40 | 41 | if isequal(C{1},'FORMATLAB') 42 | na = fieldnames(importlist); 43 | C = C(2:end); 44 | na = na(2:end); 45 | dataind = findrow('21',na); % data start index 46 | 47 | 48 | for k=dataind:size(C,1) 49 | if isvector(C{k}) %migth be a multiarray 50 | ind = findrow(['1' na{k}(2)],na); 51 | 52 | if ~isequal(C{ind},'NO_MULTI') %it's a multiarray 53 | d = C{ind}'; % the dimensions needed to reshape the multimatrix 54 | C{k} = reshape(C{k},d); 55 | 56 | elseif ~ischar(C{k}) 57 | 58 | C{k} = double(C{k}); 59 | end 60 | end 61 | end 62 | 63 | 64 | C = C(dataind:size(C,1)); 65 | end 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | end 75 | 76 | -------------------------------------------------------------------------------- /reach/inst/matlab/rList2Cell.m: -------------------------------------------------------------------------------- 1 | function [ C ] = rList2Cell( importlist ) 2 | % Transforms a R list datatype (which is imported as a struct) to 3 | % a Matlab cell-array. Also recovers/ reformats multi-arrays contained 4 | % in this R list (which are only exported as vectors). 5 | % 6 | % importlist the imported struct equivalent of the R list, 7 | % which was reformated in R using 8 | % matlabExportList.R and exported to Matlab using 9 | % writeMat() from the R.matlab package 10 | % 11 | % 12 | % 13 | % 14 | % 15 | % 16 | % 17 | % C a Matlab cell-array containing in each cell the 18 | % corresponding element of the original R list data 19 | % (before it was reformated using 20 | % matlabExportList.R()); also multi-arrays stored in 21 | % the original R list datatype are recovered 22 | % 23 | % USAGE: 24 | % 25 | % C = rList2Cell(importlist) 26 | % 27 | % 28 | % See also: matlabExportList.R 29 | % 30 | % The MIT License (MIT) (http://opensource.org/licenses/MIT) 31 | % Copyright (c) 2015 Christoph Schmidt 32 | 33 | % 21.11.14 34 | 35 | C = struct2cell(importlist); 36 | 37 | 38 | 39 | 40 | 41 | if isequal(C{1},'FORMATLAB') 42 | na = fieldnames(importlist); 43 | C = C(2:end); 44 | na = na(2:end); 45 | dataind = findrow('21',na); % data start index 46 | 47 | 48 | for k=dataind:size(C,1) 49 | if isvector(C{k}) %migth be a multiarray 50 | ind = findrow(['1' na{k}(2)],na); 51 | 52 | if ~isequal(C{ind},'NO_MULTI') %it's a multiarray 53 | d = C{ind}'; % the dimensions needed to reshape the multimatrix 54 | C{k} = reshape(C{k},d); 55 | 56 | elseif ~ischar(C{k}) 57 | 58 | C{k} = double(C{k}); 59 | end 60 | end 61 | end 62 | 63 | 64 | C = C(dataind:size(C,1)); 65 | end 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | end 75 | 76 | -------------------------------------------------------------------------------- /reach/man/runMatlabScript.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/runMatlabScript.R 3 | \name{runMatlabScript} 4 | \alias{runMatlabScript} 5 | \title{Starts Matlab on the R console and executes a Matlab script file} 6 | \usage{ 7 | runMatlabScript(scriptName) 8 | } 9 | \arguments{ 10 | \item{scriptName}{String denoting the .m script (with or without the file 11 | extension)} 12 | } 13 | \description{ 14 | Starts Matlab on the R console (or for MS Windows: in an extra Matlab console 15 | window), executes the input Matlab script file (.m file) and quits Matlab. 16 | } 17 | \details{ 18 | As R and Matlab cannot directly exchange data natively, no value 19 | will be returned directly. Instead, let Matlab save the results of its 20 | computations and load these into R for further processing. See also the 21 | following system call example: 22 | system('/Applications/MATLAB_R2013a.app/bin/matlab -nosplash -nodesktop -r 23 | "S_test; quit;"') An error in the Matlab script prevents Matlab from 24 | quitting in the R console and might require killing the Matlab process or a 25 | re-start of the R session. So check the script in Matlab before executing 26 | it within R. 27 | } 28 | \note{ 29 | The function expects the script to be saved in the current R working 30 | directory. The script file might as well be generated by R code on the fly 31 | as shown in the examples section. 32 | } 33 | \examples{ 34 | \dontrun{ 35 | 36 | scriptName <- "myscript.m" 37 | mypath <- getwd() 38 | print(mypath) 39 | scriptCode <- "pwd, x=1:2:7; y=3; z=x.^y; save xyz.mat x y z -v7" 40 | writeLines(scriptCode, con=scriptName) 41 | list.files(mypath) 42 | 43 | runMatlabScript(scriptName) 44 | 45 | list.files(mypath) 46 | system(paste("rm ", scriptName, sep="")) 47 | inp <- R.matlab::readMat("xyz.mat") 48 | str(inp) 49 | system("rm xyz.mat") 50 | list.files(mypath) 51 | } 52 | 53 | } 54 | \author{ 55 | Christoph Schmidt 56 | } 57 | \seealso{ 58 | \code{\link{convert2RData}}, \code{\link{runMatlabCommand}} 59 | } 60 | 61 | -------------------------------------------------------------------------------- /reach/man/matlabExportList.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/matlabExportList.R 3 | \name{matlabExportList} 4 | \alias{matlabExportList} 5 | \title{Reformats an R list to be exported to Matlab as cell-array} 6 | \usage{ 7 | matlabExportList(rlist) 8 | } 9 | \arguments{ 10 | \item{rlist}{List that is exported to Matlab as cell-array} 11 | } 12 | \value{ 13 | This function returns a reformated list that can be exported to 14 | Matlab with writeMat(). In Matlab it should be transformed to a cell-array 15 | using the Matlab function 'rList2Cell()', which is distributed with this 16 | package. 17 | } 18 | \description{ 19 | Exporting a R list with unnamed entries to Matlab using the 20 | R.matlab::writeMat function yields a Matlab struct with no fields (an empty 21 | struct). With the help of the matlabExportList function such a R list is 22 | reformated so that the export results in a struct with fields (1,2,...), 23 | accessible with the Matlab getfield() function. In Matlab, the loaded struct 24 | can then be further processed with the Matlab function 'rList2Cell()', which 25 | is distributed with this package, to yield a Matlab cell-array. For export, 26 | the package 'R.matlab' has to be used. Note that in particular for storing a 27 | multidimensional matrix one can also use R arrays instead of lists 28 | (a<-array(dim=c(3,3,2); a[,,1]<-matrix(99,3,3); ...) which will be exported 29 | just fine to Matlab and don't need any further processing. 30 | } 31 | \details{ 32 | A list containing lists is not supported by the writeMat() function 33 | and provokes an error. Consequently, this is checked for in 34 | matlabExportList and triggers an error. 35 | } 36 | \examples{ 37 | \dontrun{ 38 | 39 | rlist <- list(matrix(sample(100,16),4,4), c(1,2,3,4), "somestring") 40 | print(rlist) 41 | matlablist <- matlabExportList(rlist) 42 | print(matlablist) 43 | R.matlab::writeMat("test.mat", myexportdata=matlablist) 44 | # in Matlab or using runMatlabCommand() (and having "rList2Cell.m" on the Matlab path): 45 | runMatlabCommand("load test.mat; myexportdata, rc=rList2Cell(myexportdata); celldisp(rc); quit") 46 | system("rm test.mat") 47 | } 48 | 49 | } 50 | \author{ 51 | Christoph Schmidt 52 | } 53 | \seealso{ 54 | \code{\link{rList2Cell}} 55 | } 56 | 57 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | ## reach 0.4.5 2 | ##### Added Microsoft Windows support 3 | - added Windows support for ```runMatlabFct()```, ```runMatlabCommand()``` and ```runMatlabScript()``` 4 | - Windows support was successfully tested on Microsoft Windows 7 Professional running Matlab R2014a 5 | - Windows support was successfully tested on Microsoft Windows 10 Home running Matlab R2016a 6 | 7 | 8 | ##### Features currently in development 9 | - future releases of 'reach' will be able to read and write Matlab -v7.3 mat-files (HDF5); actually 'reach' will be able to transparently read -v7 / -v7.3 Matlab files 10 | - future releases of 'reach' will wrap generated Matlab code in try-catch blocks so that the Matlab process will automatically terminate in case of encountered errors 11 | 12 | 13 | 14 | 15 | ## reach 0.4.2 16 | - new ```matWrite()``` function for conveniently writing .mat files and correctly writing .mat files containing logicals and atomic vectors 17 | - ```matWrite()``` replaces R.matlab::writeMat() in ```runMatlabFct()``` 18 | - ```runMatlabFct()``` uses temporary .mat files with random names (instead of fixed names) for data transfer to Matlab, making running several instances of this function robust against overwriting these files 19 | - ```runMatlabFct()``` will now throw an error if Matlab function input arguments are named like 'writeMat()' options, which cannot be written to .mat files and thus cannot be exported to Matlab this way 20 | - ```convert2RData()``` was rewritten and the way it is used changed slightly. Sorry for the inconvenience. 21 | 22 | 23 | ## reach 0.3.1 24 | - fixed: temporary files were not removed by ```runMatlabFct()``` under certain circumstances 25 | - ```runMatlabScript()``` now complains if the input Matlab script does not exist in the current working directory, therefore one does not have to wait for Matlab to throw the error 26 | - the ```rList2Cell()``` Matlab function is now also bundled with the R package in inst/matlab/ 27 | 28 | 29 | ## reach 0.3.0 30 | - reach can now seamlessly call Matlab functions and return their results directly using ```runMatlabFct()``` 31 | - to give an example: 32 | 33 | ``` 34 | a <- c(1,2,1,4,1,5,4,3,2,2,1,6,3,1,3,5,5) 35 | b <- c(4,6,9) 36 | results <- runMatlabFct( "[bool, pos] = ismember(b,a)" ) 37 | ``` 38 | 39 | returns the following results 40 | 41 | ``` 42 | > results 43 | $bool 44 | [,1] 45 | [1,] 1 46 | [2,] 1 47 | [3,] 0 48 | 49 | $pos 50 | [,1] 51 | [1,] 4 52 | [2,] 12 53 | [3,] 0 54 | ``` 55 | 56 | Plots using Matlab are possible, too: 57 | 58 | ``` 59 | p <- runMatlabFct( "[X,Y,Z] = peaks(25)" ) 60 | X <- p$X 61 | Y <- p$Y 62 | Z <- p$Z 63 | runMatlabFct( "surf(X,Y,Z)" ) 64 | ``` 65 | 66 | ![3-D shaded surface plot](surf.png "3-D shaded surface plot") -------------------------------------------------------------------------------- /reach/R/runMatlabScript.R: -------------------------------------------------------------------------------- 1 | #' Starts Matlab on the R console and executes a Matlab script file 2 | #' 3 | #' Starts Matlab on the R console (or for MS Windows: in an extra Matlab console 4 | #' window), executes the input Matlab script file (.m file) and quits Matlab. 5 | #' 6 | #' @note The function expects the script to be saved in the current R working 7 | #' directory. The script file might as well be generated by R code on the fly 8 | #' as shown in the examples section. 9 | #' 10 | #' @param scriptName String denoting the .m script (with or without the file 11 | #' extension) 12 | #' 13 | #' @details As R and Matlab cannot directly exchange data natively, no value 14 | #' will be returned directly. Instead, let Matlab save the results of its 15 | #' computations and load these into R for further processing. See also the 16 | #' following system call example: 17 | #' system('/Applications/MATLAB_R2013a.app/bin/matlab -nosplash -nodesktop -r 18 | #' "S_test; quit;"') An error in the Matlab script prevents Matlab from 19 | #' quitting in the R console and might require killing the Matlab process or a 20 | #' re-start of the R session. So check the script in Matlab before executing 21 | #' it within R. 22 | #' 23 | #' @seealso \code{\link{convert2RData}}, \code{\link{runMatlabCommand}} 24 | #' 25 | #' @export 26 | #' 27 | #' @examples 28 | #' \dontrun{ 29 | #' 30 | #' scriptName <- "myscript.m" 31 | #' mypath <- getwd() 32 | #' print(mypath) 33 | #' scriptCode <- "pwd, x=1:2:7; y=3; z=x.^y; save xyz.mat x y z -v7" 34 | #' writeLines(scriptCode, con=scriptName) 35 | #' list.files(mypath) 36 | #' 37 | #' runMatlabScript(scriptName) 38 | #' 39 | #' list.files(mypath) 40 | #' system(paste("rm ", scriptName, sep="")) 41 | #' inp <- R.matlab::readMat("xyz.mat") 42 | #' str(inp) 43 | #' system("rm xyz.mat") 44 | #' list.files(mypath) 45 | #' } 46 | #' 47 | #' @author Christoph Schmidt 48 | 49 | # 03.06.16 50 | 51 | 52 | runMatlabScript <- function(scriptName){ 53 | #### Checking whether .m was appended to the scriptName #### 54 | if(stringr::str_detect(scriptName, ".m")){ # TRUE: '.m' has to be removed 55 | scriptName <- stringr::str_sub(scriptName, start = 1L, end = -3L) 56 | } 57 | 58 | 59 | if(!file.exists(paste(scriptName, ".m", sep = ""))){ 60 | stop("Input Matlab script does not exist in current working directory.") 61 | } 62 | 63 | 64 | #### Selecting Matlab App #### 65 | matlabCall <- getMatlabCall() 66 | 67 | 68 | 69 | 70 | #### Starting Matlab #### 71 | mypath <- getwd() 72 | ind <- stringr::str_locate(mypath, " ") 73 | 74 | 75 | if(!is.na(ind[1,1])){ # path contains spaces/blanks (is not consecutive) 76 | tmp1 <- stringr::str_sub(mypath, 1, ind[1,1]-1) 77 | tmp2 <- stringr::str_sub(mypath, ind[1,1], stringr::str_length(mypath)) 78 | mypath <- stringr::str_c(tmp1, "'", tmp2, "'") # Matlab treats this as a single input argument 79 | } 80 | 81 | 82 | matlabRun <- paste(matlabCall, " -nosplash -nodesktop -r \"cd ", mypath, 83 | "; ", scriptName, "; quit\"", sep="") 84 | 85 | cat(paste(matlabRun, "\n\n")) 86 | system(matlabRun) 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /reach/man/runMatlabCommand.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/runMatlabCommand.R 3 | \name{runMatlabCommand} 4 | \alias{runMatlabCommand} 5 | \title{Starts Matlab on the R console and executes one or several input Matlab 6 | commands} 7 | \usage{ 8 | runMatlabCommand(commandName, verbose = FALSE, do_quit = TRUE) 9 | } 10 | \arguments{ 11 | \item{commandName}{a string denoting the Matlab command/ commands} 12 | 13 | \item{verbose}{logical indicating if the final internally generated Matlab 14 | command should be printed to the R console} 15 | 16 | \item{do_quit}{logical indicating if the Matlab process should quit itself. 17 | The default is TRUE, however, if the Matlab command is a plot function then 18 | one wants Matlab to keep displaying the plot window and not quit. This 19 | means the user has to quit Matlab manually prior to continue working in the 20 | current R session.} 21 | } 22 | \description{ 23 | Starts Matlab on the R console and let it executes the input Matlab command 24 | or several input commands, like function calls (separated by ";") and quits 25 | Matlab. No values are directly returned back to the R session. To achieve 26 | this, use the function \code{runMatlabFct()}. Discerns the OS X, Linux and MS 27 | Windows Matlab app shell command. Automatically changes to the current R 28 | working directory in Matlab so that .mat files with Matlab results would be 29 | saved there instead of the default Matlab working directory. 30 | } 31 | \details{ 32 | As R and Matlab cannot directly exchange data natively, no value can 33 | be returned. Instead, let Matlab save the results of its computations and 34 | load these into R for further processing. An error in the Matlab command 35 | prevents Matlab from quitting in the R console and might require killing 36 | the Matlab process or an re-start of the R session. (You migth want to 37 | check the command in Matlab before executing it within R.) The commandName 38 | could look something like this: "load someData.mat; [out1, 39 | out2]=someMatlabFunction(in1, in2, in3); save someData2.mat; quit" 40 | } 41 | \examples{ 42 | \dontrun{ 43 | 44 | commandName <- "x=1:2:7; y=3; disp(x); disp(x.^y); quit" 45 | runMatlabCommand(commandName) 46 | 47 | 48 | 49 | commandName2 <- "M=magic(4); disp(M); eig(M)" 50 | runMatlabCommand(commandName2) 51 | 52 | 53 | 54 | wrong_but_corrected_commandName <- "M=magic(4); disp(M); eig(M) quit" 55 | runMatlabCommand(wrong_but_corrected_commandName) 56 | 57 | 58 | 59 | commandName3 <- "A=magic(3); save('magic.mat', 'A', '-v7'); quit" 60 | runMatlabCommand(commandName3) 61 | input <- R.matlab::readMat("magic.mat") 62 | print(input$A) 63 | invisible(capture.output(file.remove("magic.mat"))) 64 | 65 | 66 | 67 | # !the Matlab process has to be terminated manually! 68 | commandName4 <- "A=magic(40); imagesc(A)" 69 | runMatlabCommand(commandName4, do_quit = FALSE) 70 | 71 | 72 | 73 | # !the Matlab process has to be terminated manually! 74 | commandName4 <- "spy; quit" # quit will be internally removed 75 | runMatlabCommand(commandName4, do_quit = FALSE) 76 | 77 | } 78 | 79 | } 80 | \author{ 81 | Christoph Schmidt 82 | } 83 | \seealso{ 84 | \code{\link{runMatlabFct}}, \code{\link{runMatlabScript}} 85 | } 86 | 87 | -------------------------------------------------------------------------------- /reach/man/convert2RData.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/convert2RData.R 3 | \name{convert2RData} 4 | \alias{convert2RData} 5 | \title{Converts a Matlab data file (.mat) to R data file (.RData)} 6 | \usage{ 7 | convert2RData(matfile = NULL, dir = "./", verbose = FALSE) 8 | } 9 | \arguments{ 10 | \item{matfile}{character or character vector denoting one or several 11 | matfiles, e.g. "mymatlabdata", "mymatlabdata.mat", c("one.mat", "two.mat"). 12 | Providing the file type specifier ".mat" is optional. Defaults to NULL, 13 | which means that all .mat files in the specified directory \code{dir} will 14 | be converted.} 15 | 16 | \item{dir}{path to a directory which contains one or several .mat files that 17 | should be converted to .RData files. Defaults to the current working 18 | directory.} 19 | 20 | \item{verbose}{logical indicating whether console output about the conversion 21 | process should be printed} 22 | } 23 | \description{ 24 | Converts all or specified Matlab .mat files in the specified directory to 25 | .RData files, while keeping the .mat files unchanged. The Matlab files must 26 | have been saved in a MAT file format version supported by R.matlab's 27 | \code{readMat} function (e.g. saved with the -v7 option flag, but not with 28 | the -v7.3 flag). 29 | } 30 | \examples{ 31 | \dontrun{ 32 | 33 | ##### conversion: specified .mat file in current working directory #### 34 | v <- sample(1:10,4) 35 | m <- matrix(runif(9),3,3) 36 | R.matlab::writeMat("file_convert2RData.mat", v=v, m=m) 37 | rm(v,m) 38 | convert2RData("file_convert2RData.mat") 39 | load("file_convert2RData.RData") 40 | print(v) 41 | print(m) 42 | file.remove(c("file_convert2RData.RData", "file_convert2RData.mat")) 43 | 44 | 45 | 46 | #### conversion: all .mat files in a specified directory #### 47 | this_dir <- getwd() 48 | m <- matrix(runif(9),3,3) 49 | v <- seq(1,100) 50 | R.matlab::writeMat("dir_convert2RData_1.mat", m=m) 51 | R.matlab::writeMat("dir_convert2RData_2.mat", v=v) 52 | rm(v,m) 53 | convert2RData(dir = this_dir, verbose = TRUE) 54 | load(paste(this_dir, "/dir_convert2RData_1.Rdata", sep = "")) 55 | print(m) 56 | load(paste(this_dir, "/dir_convert2RData_2.Rdata", sep = "")) 57 | print(v) 58 | file.remove(c(paste(this_dir, "/dir_convert2RData_1.mat", sep = ""), 59 | paste(this_dir, "/dir_convert2RData_2.mat", sep = ""), 60 | paste(this_dir, "/dir_convert2RData_1.Rdata", sep = ""), 61 | paste(this_dir, "/dir_convert2RData_2.Rdata", sep = ""))) 62 | 63 | 64 | 65 | #### conversion: specified .mat file in a specified directory #### 66 | this_dir <- getwd() 67 | v <- seq(1,10) 68 | R.matlab::writeMat("file_dir_convert2RData.mat", v=v) 69 | rm(v) 70 | convert2RData("file_dir_convert2RData.mat", this_dir) 71 | load("file_dir_convert2RData.RData") 72 | print(v) 73 | file.remove(c("file_dir_convert2RData.mat", "file_dir_convert2RData.RData")) 74 | 75 | 76 | 77 | #### conversion: several specified .mat files in current working directory #### 78 | v <- sample(1:10,4) 79 | m <- matrix(runif(9),3,3) 80 | R.matlab::writeMat("twofiles_convert2RData_1.mat", v=v) 81 | R.matlab::writeMat("twofiles_convert2RData_2.mat", m=m) 82 | rm(v,m) 83 | convert2RData(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat")) 84 | load("twofiles_convert2RData_1.RData") 85 | print(v) 86 | load("twofiles_convert2RData_2.RData") 87 | print(m) 88 | file.remove(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat", 89 | "twofiles_convert2RData_1.RData", "twofiles_convert2RData_2.RData")) 90 | } 91 | 92 | } 93 | \author{ 94 | Christoph Schmidt 95 | } 96 | 97 | -------------------------------------------------------------------------------- /reach/R/matWrite.R: -------------------------------------------------------------------------------- 1 | #' Writes .mat files for exporting data to be used with Matlab 2 | #' 3 | #' Writes .mat files to store R session data using the R.matlab package and 4 | #' takes care that logicals and atomic vectors are saved properly: currently, 5 | #' R.matlab does not write logicals and atomic vectors (not 1D arrays/ matrices) 6 | #' in a way that they can be opened properly in Matlab (logicals will not be 7 | #' stored and atomic vectors will be transposed in the Matlab session - but they 8 | #' appear untransposed when read back from the .mat file into R using 9 | #' R.matlab::readMat()). This function is a convenient wrapper for 10 | #' R.matlab::writeMat() that stores logicals as 0 / 1 and that transposes atomic 11 | #' vectors when saving the matfile. 12 | #' 13 | #' @param fn file name, a character string 14 | #' 15 | #' @param vars character vector containing a comma separated listing of 16 | #' variables to be saved in the matfile. The variables have to exist in the 17 | #' environment where the function was called from. 18 | #' 19 | #' @export 20 | #' 21 | #' @examples 22 | #' A <- matrix(c(2,3,4,2, 1,1,2,6, 8,3,9,7), 3, 4, byrow = TRUE) 23 | #' b <- c(3, 5, 1, 9, 18, 2) # atomic vector 1x6 24 | #' cc <- array(b, c(1, length(b))) # array vector 1x6 25 | #' dd <- t(cc) # array vector 6x1 26 | #' myChar <- c("from", "R", "to", "Matlab") 27 | #' bool <- TRUE # logical 28 | #' k <- FALSE 29 | #' l <- FALSE 30 | #' fn <- "mytestmat" 31 | #' vars <- "A, b, cc, dd, myChar, bool, k, l" 32 | #' matWrite(fn, vars) 33 | #' unlink(paste(fn, ".mat", sep = "")) 34 | #' 35 | #' @author Christoph Schmidt 36 | 37 | # 17.12.15 38 | 39 | matWrite <- function(fn, vars){ 40 | if(!stringr::str_detect(fn, "^.*\\.mat$")){ 41 | fn <- paste(fn, ".mat", sep = "") 42 | } 43 | 44 | 45 | 46 | 47 | varsList <- str_extractCommaSepArgs(vars) 48 | saveVars <- "" 49 | 50 | # strange iterator name to circumvent manipulating a global variable that should 51 | # be saved to .mat, e.g. k, i, ... 52 | for(kqzuacrlk in 1:length(varsList)){ 53 | tmpStr <- varsList[[kqzuacrlk]] 54 | tmpStr_ <- paste(tmpStr, '_', sep = "") # for storing local copy of global variable from parent frame 55 | 56 | if( !exists(tmpStr, envir = parent.frame()) ){ 57 | stop(paste("Input variable name: '", tmpStr, 58 | "'\ndoes not correspond to a variable stored in the parent frame.", sep = "")) 59 | } 60 | 61 | 62 | tmpVal <- get(tmpStr, envir = parent.frame()) 63 | 64 | 65 | # is.vector(list) = TRUE, is.vector(logical) = TRUE, is.vector(array) = FALSE, is.list(vector) = FALSE 66 | # does not transpose one-dimensional matrices / arrays (they don't need to be transformed) 67 | if(is.vector(tmpVal) && !is.list(tmpVal) && !is.logical(tmpVal)){ 68 | assign(tmpStr_, t(tmpVal)) # create new local variable containing the transpose of the corresponding parent.frame atomic vector 69 | } 70 | else if(is.logical(tmpVal)){ 71 | if(tmpVal){ 72 | assign(tmpStr_, 1) # create new local variable containing the corresponding integer encoding of the corresponding parent.frame logical 73 | } 74 | else { 75 | assign(tmpStr_, 0) 76 | } 77 | } 78 | else { 79 | assign(tmpStr_, tmpVal) 80 | } 81 | 82 | 83 | saveVars <- paste(saveVars, tmpStr, " = ", tmpStr_, ",", sep = "") 84 | } 85 | 86 | 87 | saveVars <- stringr::str_sub(saveVars, end = -2L) # deleting last comma 88 | 89 | 90 | 91 | ### saving .mat file --- 92 | f <- paste("R.matlab::writeMat('", fn, "', ", saveVars, ")", sep = "") 93 | eval(parse(text=f)) 94 | } 95 | -------------------------------------------------------------------------------- /reach/man/runMatlabFct.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/runMatlabFct.R 3 | \name{runMatlabFct} 4 | \alias{runMatlabFct} 5 | \title{Runs a Matlab function like an R function and returns its results} 6 | \usage{ 7 | runMatlabFct(fcall) 8 | } 9 | \arguments{ 10 | \item{fcall}{string specifying the Matlab function call with output and input 11 | parameters, just as one would call the function inside a regular Matlab 12 | session. The input arguments specified in fcall must be variable names 13 | stored in the current R session (the environment where the function was 14 | called from) or numeric values. Variable names are preferred and should be 15 | the standard for using this function. If no output parameter are contained 16 | in fcall, then it is assumed that the user expects Matlab to not 17 | automatically quit (e.g. because the function call was a plotting function 18 | and the plot window should stay open). In this case the Matlab process has 19 | to be terminated manually (!) by the user before the function can terminate 20 | and one can continue to work in the R session. Nested Matlab function calls 21 | (function as input argument for a function) are currently not supported.} 22 | } 23 | \value{ 24 | The results of the Matlab function call. A list of named entries that 25 | correspond to the output arguments of the Matlab function as specified in 26 | \emph{fcall}. 27 | } 28 | \description{ 29 | Runs Matlab on the R console, evaluates the specified Matlab function with 30 | given arguments and directly returns the Matlab output as a single value or a 31 | list of values back to the R session. The function acts as a proxy for the 32 | specified Matlab function. It handles all Matlab related things internally 33 | and transparently to the user and just returns the specified Matlab output 34 | arguments as results of the computations performed by Matlab. 35 | } 36 | \details{ 37 | This function calls the user specified Matlab function and manages 38 | the necessary data exchange transparently, thus providing a seamless 39 | experience. The data is exchanged robustly over the file system, using 40 | temporary files that will be deleted automatically. 41 | } 42 | \note{ 43 | For starting several Matlab functions independently of each other, or a 44 | chain of Matlab functions, consider using the 'runMatlabCommand()' 45 | function, or writing a Matlab script file and use the 'runMatlabScript()' 46 | function and store the results in a .mat file that can be read back into R 47 | with the help of the 'convert2RData()' function. 48 | } 49 | \examples{ 50 | \dontrun{ 51 | a <- c(1,2,1,4,1,5,4,3,2,2,1,6,3,1,3,5,5) 52 | b <- c(4,6,9) 53 | fcall <- "[bool, pos] = ismember(b,a)" 54 | 55 | results <- runMatlabFct(fcall) 56 | 57 | bool <- results$bool 58 | pos <- results$pos 59 | print(a) 60 | print(b) 61 | print(fcall) 62 | print(bool) 63 | print(pos) 64 | 65 | 66 | 67 | # !the Matlab process has to be terminated manually! 68 | runMatlabFct("image") 69 | 70 | 71 | 72 | # !the Matlab process has to be terminated manually! 73 | # wrong Matlab function input ( it should be penny or penny() ), but corrected internally 74 | runMatlabFct("penny(") 75 | 76 | 77 | 78 | M <- matrix(c(2,-1,0, -1,2,-1, 0,2,3), 3, 3) 79 | fcall <- "C = chol(M)" 80 | results <- runMatlabFct(fcall) 81 | print(results) 82 | 83 | 84 | 85 | A <- runMatlabFct("A=rand(6)")$A 86 | A_ <- runMatlabFct("A_=inv(A)")$A_ 87 | print(round(A \%*\% A_)) 88 | 89 | 90 | 91 | nu_ <- 0.32 92 | vec_ <- c(1,2,5,2,6) 93 | res_ <- runMatlabFct("v_ = bessely(nu_, vec_)") 94 | print(res_$v_) 95 | 96 | 97 | 98 | orig_str <- 'this_test_was_my_first_test' 99 | old_sub <- 'test' 100 | new_sub <- 'assignment' 101 | new_str <- runMatlabFct('str=strrep(orig_str, old_sub, new_sub)') 102 | print(new_str$str) 103 | 104 | } 105 | 106 | } 107 | \author{ 108 | Christoph Schmidt 109 | } 110 | \seealso{ 111 | \code{\link{runMatlabScript}}, \code{\link{runMatlabCommand}} 112 | } 113 | 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/schmidtchristoph/reach.svg?branch=master)](https://travis-ci.org/schmidtchristoph/reach) 2 | 3 | # reach: 4 | ## R < > Matlab interoperability 5 | ##### version 0.4.5 6 | 7 | The reach package contains functions that make it easy to reuse existing Matlab code within R code and to build a robust integrated R — Matlab software pipeline and workflow, enhancing the compatibility between R and Matlab. It enables to run Matlab functions and scripts from within R using .mat files as a means to robustly exchange data between R and a separate Matlab process. The 'reach' functions just work, with no complicated setup required. However, the robust data transfer via the file system comes at the price of an overhead to start and terminate the Matlab process for every function call or Matlab script execution. Primarily, the reach package is meant to be used to batch process a lot of data in each executed Matlab script or to run Matlab functions that have high time complexity. For such computations a few Matlab restarts as well as reading/ writing data to and from the disk (SSD) are neglectable. With 'reach' one can start a chain of R/ Matlab scripts, which can execute and control computations that might take weeks in a row without any further manual intervention from the side of the user. 8 | 9 | There are currently functions for 10 | 11 | - executing Matlab functions seamlessly as if they were R functions and returning their results directly into the R session (OS X, Linux, Windows) 12 | - converting .mat files into .RData files as well as batch converting all .mat files contained within a folder (OS X, Linux, Windows) 13 | - starting Matlab scripts, which can save the results for importing them back into R (OS X, Linux, Windows) 14 | - starting Matlab functions and printing their results on the terminal (OS X, Linux, Windows) 15 | - exporting R lists with unnamed entries to Matlab and recovering multi-dimensional matrices in Matlab contained in exported R lists (OS X, Linux, Windows) 16 | 17 | To give an example of seamlessly executing a Matlab function and returning the results directly back into the R session using ```runMatlabFct()```: 18 | ``` 19 | a <- c(1,2,1,4,1,5,4,3,2,2,1,6,3,1,3,5,5) 20 | b <- c(4,6,9) 21 | results <- runMatlabFct( "[bool, pos] = ismember(b,a)" ) 22 | ``` 23 | 24 | returns the following results 25 | 26 | ``` 27 | > results 28 | $bool 29 | [,1] 30 | [1,] 1 31 | [2,] 1 32 | [3,] 0 33 | 34 | $pos 35 | [,1] 36 | [1,] 4 37 | [2,] 12 38 | [3,] 0 39 | ``` 40 | 41 | Plots using Matlab are possible, too: 42 | 43 | ``` 44 | p <- runMatlabFct( "[X,Y,Z] = peaks(25)" ) 45 | X <- p$X 46 | Y <- p$Y 47 | Z <- p$Z 48 | runMatlabFct( "surf(X,Y,Z)" ) 49 | ``` 50 | 51 | ![3-D shaded surface plot](surf.png "3-D shaded surface plot") 52 | 53 | 54 | For further details and documentation of all package functions please refer to the reference manual "reach.pdf". 55 | 56 | --- 57 | 58 | ##### Note: 59 | 60 | R and Matlab cannot directly exchange data, making reading and writing .mat files within R sessions necessary, for which 'reach' relies on the R.matlab package (on [CRAN](http://cran.r-project.org/web/packages/R.matlab/index.html) and also on [Github](https://github.com/HenrikBengtsson/R.matlab/)). Using R.matlab it is also possible to start a Matlab server to let R communicate with Matlab. However, I found this not always working reliably, especially when using different machines (like switching to a computer cluster) to run the R code. The R.matlab package manual states that „The R to MATLAB interface, that is the Matlab class, is less prioritized and should be considered a beta version.“. Therefore, I adopted the strategy to write Matlab scripts and run them from within R using functions of the reach package. I write these Matlab scripts to save results as .mat files and to quit the Matlab process. I then instruct R to read the .mat files after Matlab terminated, then to further process their data and to finally delete them. Thus, the data exchange between R and Matlab is indirect, but robust, using the file system. 61 | 62 | - - - 63 | 64 | ### How to install this package from GitHub 65 | 66 | There are several ways of installing the package, e.g.: 67 | 68 | - install the "devtools" package first, then use devtools::install_github("schmidtchristoph/reach/reach") 69 | 70 | - download the source package, then run on the command line: R CMD INSTALL /path/to/reach_0.4.5.tar.gz 71 | 72 | - install the "devtools" package first, clone the repository, then use devtools::install("path/to/repository/reach") 73 | 74 | - - - 75 | 76 | The package passes devtools::check( ) with zero problems, notes or warnings on my machine running R 3.2.4 on OS X 10.10.5. 77 | 78 | The 'dev' branch can be considered stable, even though it contains incomplete new features. Generally, only changes with devtools::check( ) Status: OK are pushed. 79 | 80 | - - - 81 | The [MIT License (MIT)](http://opensource.org/licenses/MIT) 82 | Copyright (c) 2016 Christoph Schmidt 83 | -------------------------------------------------------------------------------- /reach/tests/testthat/test_matWrite.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(reach) 3 | context("matWrite") 4 | 5 | 6 | test_that("atomic vectors are transposed and properly imported into Matlab", { 7 | testthat::skip_on_travis() 8 | 9 | vec <- sample(100, 13) # 1x13, dim(vec) = NULL >>> atomic vectors don't have dimensions, arrays do 10 | matWrite("testthat.mat", "vec") 11 | inp <- R.matlab::readMat("testthat.mat") # vec is transformed to an array 12 | vec2 <- inp$vec 13 | 14 | expect_equal(length(vec), dim(vec2)[2]) 15 | 16 | script <- "load testthat.mat;\nthisSize = num2str(size(vec));\nsave testthat_back.mat thisSize;" 17 | writeLines(script, con = "script.m") 18 | reach::runMatlabScript("script.m") 19 | 20 | inp2 <- R.matlab::readMat("testthat_back.mat") 21 | thisSize <- inp2$thisSize 22 | 23 | expect_equal(as.vector(thisSize), "1 13") 24 | 25 | 26 | 27 | 28 | vec <- t(sample(100, 6)) # "vector": t(1x6) = 6x1, but is transformed to an ARRAY (is.vector(vec) = FALSE): dim(vec) = 1 6 29 | matWrite("testthat.mat", "vec") # the array vec will be written correctly by R.matlab::writeMat() anyway & will not be transposed, too... 30 | inp <- R.matlab::readMat("testthat.mat") 31 | vec2 <- inp$vec 32 | 33 | expect_equal(length(vec), dim(vec2)[2]) 34 | 35 | script <- "load testthat.mat;\nthisSize = num2str(size(vec));\nsave testthat_back.mat thisSize;" 36 | writeLines(script, con = "script.m") 37 | reach::runMatlabScript("script.m") 38 | 39 | inp2 <- R.matlab::readMat("testthat_back.mat") 40 | thisSize <- inp2$thisSize 41 | 42 | expect_equal(as.vector(thisSize), "1 6") 43 | 44 | 45 | 46 | unlink(c("testthat.mat", "testthat_back.mat", "script.m")) 47 | }) 48 | 49 | 50 | 51 | 52 | 53 | test_that("one-dimensional matrices ('vectors') are properly imported to Matlab", { 54 | testthat::skip_on_travis() 55 | 56 | vec <- matrix(c(33, 12, 66, 88, 55), 1, 5) # is.array(vec) = TRUE, is.vector(vec) = FALSE >>> so no change (t()) made by matWrite() 57 | matWrite("testthat.mat", "vec") 58 | inp <- R.matlab::readMat("testthat.mat") 59 | vec2 <- inp$vec 60 | 61 | expect_equal(dim(vec), dim(vec2)) 62 | 63 | script <- "load testthat.mat;\nthisSize = num2str(size(vec));\nsave testthat_back.mat thisSize;" 64 | writeLines(script, con = "script.m") 65 | reach::runMatlabScript("script.m") 66 | 67 | inp2 <- R.matlab::readMat("testthat_back.mat") 68 | thisSize <- inp2$thisSize 69 | 70 | expect_equal(as.vector(thisSize), "1 5") 71 | 72 | 73 | 74 | 75 | 76 | vec <- matrix(c(33, 12, 66, 88, 55), 5, 1) # is.array(vec) = TRUE, is.vector(vec) = FALSE >>> so no change (t()) made by matWrite() 77 | matWrite("testthat.mat", "vec") 78 | inp <- R.matlab::readMat("testthat.mat") 79 | vec2 <- inp$vec 80 | 81 | expect_equal(dim(vec), dim(vec2)) 82 | 83 | script <- "load testthat.mat;\nthisSize = num2str(size(vec));\nsave testthat_back.mat thisSize;" 84 | writeLines(script, con = "script.m") 85 | reach::runMatlabScript("script.m") 86 | 87 | inp2 <- R.matlab::readMat("testthat_back.mat") 88 | thisSize <- inp2$thisSize 89 | 90 | expect_equal(as.vector(thisSize), "5 1") 91 | 92 | 93 | unlink(c("testthat.mat", "testthat_back.mat", "script.m")) 94 | }) 95 | 96 | 97 | 98 | 99 | 100 | test_that("logicals are replaced by 0/1 and properly imported to Matlab", { 101 | testthat::skip_on_travis() 102 | 103 | isTrue <- TRUE 104 | notTrue <- FALSE 105 | matWrite("yxc.mat", "isTrue, notTrue") 106 | inp <- R.matlab::readMat("yxc.mat") 107 | isTrue2 <- inp$isTrue 108 | notTrue2 <- inp$notTrue 109 | 110 | expect_true(isTrue2 == 1) 111 | expect_true(notTrue2 == 0) 112 | 113 | script <- "load yxc.mat;\nisTrue3 = isTrue;\nnotTrue3 = notTrue;\nsave yxcb.mat isTrue3 notTrue3;" 114 | writeLines(script, con = "script.m") 115 | reach::runMatlabScript("script.m") 116 | 117 | inp2 <- R.matlab::readMat("yxcb.mat") 118 | isTrue3 <- inp2$isTrue3 119 | notTrue3 <- inp2$notTrue3 120 | 121 | expect_true(as.vector(isTrue3) == 1) 122 | expect_true(as.vector(notTrue3) == 0) 123 | 124 | unlink(c("yxc.mat", "yxcb.mat", "script.m")) 125 | }) 126 | 127 | 128 | 129 | 130 | 131 | 132 | test_that("non-reformated variables are properly saved and imported to Matlab", { 133 | testthat::skip_on_travis() 134 | 135 | A <- matrix(c(2,3,4,2, 1,1,2,6, 8,3,9,7), 3, 4, byrow = TRUE) 136 | matWrite("testthat.mat", "A") 137 | inp <- R.matlab::readMat("testthat.mat") 138 | A2 <- inp$A 139 | 140 | expect_equal(dim(A), dim(A2)) 141 | expect_identical(A, A2) 142 | 143 | script <- "load testthat.mat;\nthisSize = num2str(size(A));\nsave testthat_back.mat thisSize;" 144 | writeLines(script, con = "script.m") 145 | reach::runMatlabScript("script.m") 146 | 147 | inp2 <- R.matlab::readMat("testthat_back.mat") 148 | thisSize <- inp2$thisSize 149 | 150 | expect_equal(as.vector(thisSize), "3 4") 151 | 152 | unlink(c("testthat.mat", "testthat_back.mat", "script.m")) 153 | }) 154 | -------------------------------------------------------------------------------- /reach/R/runMatlabCommand.R: -------------------------------------------------------------------------------- 1 | #' Starts Matlab on the R console and executes one or several input Matlab 2 | #' commands 3 | #' 4 | #' Starts Matlab on the R console and let it executes the input Matlab command 5 | #' or several input commands, like function calls (separated by ";") and quits 6 | #' Matlab. No values are directly returned back to the R session. To achieve 7 | #' this, use the function \code{runMatlabFct()}. Discerns the OS X, Linux and MS 8 | #' Windows Matlab app shell command. Automatically changes to the current R 9 | #' working directory in Matlab so that .mat files with Matlab results would be 10 | #' saved there instead of the default Matlab working directory. 11 | #' 12 | #' @param commandName a string denoting the Matlab command/ commands 13 | #' 14 | #' @param verbose logical indicating if the final internally generated Matlab 15 | #' command should be printed to the R console 16 | #' 17 | #' @param do_quit logical indicating if the Matlab process should quit itself. 18 | #' The default is TRUE, however, if the Matlab command is a plot function then 19 | #' one wants Matlab to keep displaying the plot window and not quit. This 20 | #' means the user has to quit Matlab manually prior to continue working in the 21 | #' current R session. 22 | #' 23 | #' @details As R and Matlab cannot directly exchange data natively, no value can 24 | #' be returned. Instead, let Matlab save the results of its computations and 25 | #' load these into R for further processing. An error in the Matlab command 26 | #' prevents Matlab from quitting in the R console and might require killing 27 | #' the Matlab process or an re-start of the R session. (You migth want to 28 | #' check the command in Matlab before executing it within R.) The commandName 29 | #' could look something like this: "load someData.mat; [out1, 30 | #' out2]=someMatlabFunction(in1, in2, in3); save someData2.mat; quit" 31 | #' 32 | #' @seealso \code{\link{runMatlabFct}}, \code{\link{runMatlabScript}} 33 | #' 34 | #' 35 | #' @export 36 | #' 37 | #' @examples 38 | #' \dontrun{ 39 | #' 40 | #' commandName <- "x=1:2:7; y=3; disp(x); disp(x.^y); quit" 41 | #' runMatlabCommand(commandName) 42 | #' 43 | #' 44 | #' 45 | #' commandName2 <- "M=magic(4); disp(M); eig(M)" 46 | #' runMatlabCommand(commandName2) 47 | #' 48 | #' 49 | #' 50 | #' wrong_but_corrected_commandName <- "M=magic(4); disp(M); eig(M) quit" 51 | #' runMatlabCommand(wrong_but_corrected_commandName) 52 | #' 53 | #' 54 | #' 55 | #' commandName3 <- "A=magic(3); save('magic.mat', 'A', '-v7'); quit" 56 | #' runMatlabCommand(commandName3) 57 | #' input <- R.matlab::readMat("magic.mat") 58 | #' print(input$A) 59 | #' invisible(capture.output(file.remove("magic.mat"))) 60 | #' 61 | #' 62 | #' 63 | #' # !the Matlab process has to be terminated manually! 64 | #' commandName4 <- "A=magic(40); imagesc(A)" 65 | #' runMatlabCommand(commandName4, do_quit = FALSE) 66 | #' 67 | #' 68 | #' 69 | #' # !the Matlab process has to be terminated manually! 70 | #' commandName4 <- "spy; quit" # quit will be internally removed 71 | #' runMatlabCommand(commandName4, do_quit = FALSE) 72 | #' 73 | #' } 74 | #' 75 | #' @author Christoph Schmidt 76 | 77 | # 03.06.16 78 | 79 | runMatlabCommand <- function(commandName, verbose = FALSE, do_quit = TRUE){ 80 | 81 | #### Checking whether 'quit' has to be appended to commandName #### 82 | has_quit <- stringr::str_detect(commandName, "quit") 83 | 84 | if(do_quit){ 85 | if(!has_quit){ 86 | commandName <- stringr::str_c(commandName, " ,;quit") 87 | 88 | } else { # ensure Matlab really quits: add ",;" directly preceding "quit" in case neither "," or ";" was put in front of "quit" 89 | ind2 <- stringr::str_locate(commandName, "quit") 90 | tmp1 <- stringr::str_sub(commandName, 1, ind2[1,1]-1) 91 | tmp2 <- stringr::str_sub(commandName, ind2[1,1], stringr::str_length(commandName)) 92 | commandName <- stringr::str_c(tmp1, ",;", tmp2) 93 | } 94 | 95 | } else { 96 | #### Checking whether 'quit' has to be removed from the Matlab command #### 97 | if(has_quit){ 98 | ind <- stringr::str_locate(commandName, "quit") 99 | commandName <- stringr::str_sub(commandName, 1, ind[1]-1) 100 | } 101 | } 102 | 103 | 104 | 105 | #### Checking whether the commandName ends with \" (if not, then appending it) #### 106 | lastChar <- stringr::str_sub(commandName, stringr::str_length(commandName), stringr::str_length(commandName)) 107 | 108 | if(!identical(lastChar, "\"")){ 109 | commandName <- stringr::str_c(commandName, "\"") 110 | } 111 | 112 | 113 | 114 | #### overriding Matlab default working directory #### 115 | wd <- getwd() 116 | commandName <- stringr::str_c("\"cd ", wd, "; ", commandName) 117 | 118 | 119 | 120 | #### Selecting Matlab call #### 121 | matlabCall <- getMatlabCall() 122 | 123 | 124 | flags <- " -nosplash -nodesktop -r " 125 | systemcall <- paste(matlabCall, flags, commandName, sep="") 126 | 127 | 128 | if(verbose){ print(systemcall) } 129 | 130 | 131 | system(systemcall) 132 | } 133 | 134 | -------------------------------------------------------------------------------- /reach/R/matlabExportList.R: -------------------------------------------------------------------------------- 1 | #' Reformats an R list to be exported to Matlab as cell-array 2 | #' 3 | #' Exporting a R list with unnamed entries to Matlab using the 4 | #' R.matlab::writeMat function yields a Matlab struct with no fields (an empty 5 | #' struct). With the help of the matlabExportList function such a R list is 6 | #' reformated so that the export results in a struct with fields (1,2,...), 7 | #' accessible with the Matlab getfield() function. In Matlab, the loaded struct 8 | #' can then be further processed with the Matlab function 'rList2Cell()', which 9 | #' is distributed with this package, to yield a Matlab cell-array. For export, 10 | #' the package 'R.matlab' has to be used. Note that in particular for storing a 11 | #' multidimensional matrix one can also use R arrays instead of lists 12 | #' (a<-array(dim=c(3,3,2); a[,,1]<-matrix(99,3,3); ...) which will be exported 13 | #' just fine to Matlab and don't need any further processing. 14 | #' 15 | #' @param rlist List that is exported to Matlab as cell-array 16 | #' 17 | #' @details A list containing lists is not supported by the writeMat() function 18 | #' and provokes an error. Consequently, this is checked for in 19 | #' matlabExportList and triggers an error. 20 | #' 21 | #' @return This function returns a reformated list that can be exported to 22 | #' Matlab with writeMat(). In Matlab it should be transformed to a cell-array 23 | #' using the Matlab function 'rList2Cell()', which is distributed with this 24 | #' package. 25 | #' 26 | #' @seealso \code{\link{rList2Cell}} 27 | #' 28 | #' @export 29 | #' 30 | #' @examples 31 | #' \dontrun{ 32 | #' 33 | #' rlist <- list(matrix(sample(100,16),4,4), c(1,2,3,4), "somestring") 34 | #' print(rlist) 35 | #' matlablist <- matlabExportList(rlist) 36 | #' print(matlablist) 37 | #' R.matlab::writeMat("test.mat", myexportdata=matlablist) 38 | #' # in Matlab or using runMatlabCommand() (and having "rList2Cell.m" on the Matlab path): 39 | #' runMatlabCommand("load test.mat; myexportdata, rc=rList2Cell(myexportdata); celldisp(rc); quit") 40 | #' system("rm test.mat") 41 | #' } 42 | #' 43 | #' @author Christoph Schmidt 44 | 45 | # 21.11.14 46 | 47 | matlabExportList <- function(rlist){ 48 | # CHECKING IF RLIST IS A LIST OF LISTS 49 | for (k in 1:length(rlist)) { 50 | if (is.list(rlist[[k]])) { 51 | stop('Input rlist is a list containing sub-lists. Cannot be exported with writeMat(). You should use separate lists.') 52 | } 53 | } 54 | 55 | 56 | 57 | # DETERMINING IF RLIST CONTAINS ANY MULTI-ARRAYS AND TRANSPOSING VECTOR FOR CORRECT MATLAB EXPORT 58 | ismarray <- FALSE 59 | 60 | for (k in 1:length(rlist)){ 61 | entry <- rlist[[k]] 62 | 63 | if (isvector(entry) & !is.character(entry)) { 64 | rlist[[k]] <- t(entry) # vectors are for some reason transposed upon Matlab import, so with t() here, they appear in Matlab as in rlist 65 | } 66 | 67 | 68 | if (length(dim(entry))>2 && class(entry[,,1])=='matrix') { 69 | ismarray <- TRUE 70 | } 71 | } 72 | 73 | 74 | 75 | tmp <- list() 76 | 77 | 78 | if (ismarray) { # rlist contained a multi-array 79 | tmp2 <- list() 80 | 81 | for (k in 1:length(rlist)){ 82 | if (length(dim(rlist[[k]]))>2 && class(rlist[[k]][,,1])=='matrix') { 83 | tmp[[paste(1,k,sep='')]] <- dim(rlist[[k]]) # storing dimension information for reshape 84 | 85 | } else { 86 | tmp [[paste(1,k,sep='')]] <- 'NO_MULTI' 87 | } 88 | 89 | tmp2[[paste(2,k,sep='')]] <- rlist[[k]] # the actual data 90 | } 91 | 92 | matlablist <- c('0'='FORMATLAB', tmp, tmp2) 93 | 94 | 95 | 96 | } else { 97 | for (k in 1:length(rlist)){ 98 | tmp[[as.character(k)]] <- rlist[[k]] 99 | } 100 | 101 | matlablist <- tmp 102 | } 103 | 104 | 105 | 106 | return(matlablist) 107 | } 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | #' Conversion of a R list, which was processed with the R function 123 | #' "matlabExportList()" and then imported to Matlab to a Matlab cell-array 124 | #' 125 | #' This is a Matlab function. It transforms a R list datatype (which is imported 126 | #' in Matlab as a struct) to a Matlab cell-array. Also recovers/ reformats 127 | #' multi-arrays contained in this R list (which are only exported as vectors). 128 | #' 129 | #' @param importlist the imported struct equivalent of the R list, which was 130 | #' reformated in R using matlabExportList.R and exported to Matlab using 131 | #' writeMat() from the R.matlab package 132 | #' 133 | #' @return A Matlab cell-array containing in each cell the corresponding element 134 | #' of the original R list data (before it was reformated using 135 | #' matlabExportList.R()); also multi-arrays stored in the original R list 136 | #' datatype are recovered 137 | #' 138 | #' @seealso \code{\link{matlabExportList}} 139 | #' 140 | #' @author Christoph Schmidt 141 | 142 | # 22.09.15 143 | 144 | rList2Cell <- function(importlist){ 145 | return("This really is a Matlab function.") 146 | } 147 | 148 | -------------------------------------------------------------------------------- /reach/tests/testthat/test_convert2RData.R: -------------------------------------------------------------------------------- 1 | library(reach) 2 | library(testthat) 3 | context("Conversion of Matlab .m files to R .RData files") 4 | 5 | 6 | test_that("convert2RData terminates", { 7 | 8 | ##### conversion of a single .mat file in the current working directory #### 9 | v <- sample(1:10,4) 10 | m <- matrix(runif(9),3,3) 11 | R.matlab::writeMat("file_convert2RData.mat", v=v, m=m) 12 | expect_that(convert2RData("file_convert2RData.mat"), not(throws_error())) 13 | suppressWarnings(file.remove(c("file_convert2RData.mat", "file_convert2RData.RData"))) 14 | 15 | 16 | 17 | #### conversion of all .mat files in a specified directory #### 18 | this_dir <- getwd() 19 | m <- matrix(runif(9),3,3) 20 | v <- seq(1,100) 21 | R.matlab::writeMat("dir_convert2RData_1.mat", m=m) 22 | R.matlab::writeMat("dir_convert2RData_2.mat", v=v) 23 | expect_that(convert2RData(dir=this_dir), not(throws_error())) 24 | suppressWarnings(file.remove(c("dir_convert2RData_1.mat", "dir_convert2RData_2.mat", 25 | "dir_convert2RData_1.Rdata", "dir_convert2RData_2.Rdata"))) 26 | 27 | 28 | 29 | #### conversion of all .mat files in the current working directory #### 30 | m <- matrix(runif(9),3,3) 31 | v <- seq(1,100) 32 | R.matlab::writeMat("dir_convert2RData_1.mat", m=m) 33 | R.matlab::writeMat("dir_convert2RData_2.mat", v=v) 34 | expect_that(convert2RData(), not(throws_error())) 35 | suppressWarnings(file.remove(c("dir_convert2RData_1.mat", "dir_convert2RData_2.mat", 36 | "dir_convert2RData_1.Rdata", "dir_convert2RData_2.Rdata"))) 37 | 38 | 39 | 40 | #### conversion of a single specified .mat file in a specified directory #### 41 | this_dir <- getwd() 42 | v <- seq(1,10) 43 | R.matlab::writeMat("file_dir_convert2RData.mat", v=v) 44 | expect_that(convert2RData("file_dir_convert2RData.mat", this_dir), not(throws_error())) 45 | suppressWarnings(file.remove(c("file_dir_convert2RData.mat", "file_dir_convert2RData.RData"))) 46 | 47 | 48 | 49 | #### conversion of several specified .mat files in the current working directory #### 50 | v <- sample(1:10,4) 51 | m <- matrix(runif(9),3,3) 52 | R.matlab::writeMat("twofiles_convert2RData_1.mat", v=v) 53 | R.matlab::writeMat("twofiles_convert2RData_2.mat", m=m) 54 | expect_that(convert2RData(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat")), 55 | not(throws_error())) 56 | suppressWarnings(file.remove(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat", 57 | "twofiles_convert2RData_1.RData", "twofiles_convert2RData_2.RData"))) 58 | }) 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | test_that("convert2RData produced output .RData files", { 67 | testthat::skip_on_travis() # to avoid typical Travis illogical errors 68 | 69 | ##### conversion of a single .mat file in the current working directory #### 70 | v <- sample(1:10,4) 71 | m <- matrix(runif(9),3,3) 72 | R.matlab::writeMat("file_convert2RData.mat", v=v, m=m) 73 | convert2RData("file_convert2RData.mat") 74 | bool <- suppressWarnings(file.remove(c("file_convert2RData.mat", "file_convert2RData.RData"))) 75 | expect_equal(bool, c(TRUE, TRUE)) 76 | 77 | 78 | 79 | #### conversion of all .mat files in a specified directory #### 80 | this_dir <- getwd() 81 | m <- matrix(runif(9),3,3) 82 | v <- seq(1,100) 83 | R.matlab::writeMat("dir_convert2RData_1.mat", m=m) 84 | R.matlab::writeMat("dir_convert2RData_2.mat", v=v) 85 | convert2RData(dir=this_dir) 86 | bool <- suppressWarnings(file.remove(c("dir_convert2RData_1.mat", "dir_convert2RData_2.mat", 87 | "dir_convert2RData_1.Rdata", "dir_convert2RData_2.Rdata"))) 88 | expect_equal(bool, c(TRUE, TRUE, TRUE, TRUE)) 89 | 90 | 91 | 92 | #### conversion of all .mat files in the current working directory #### 93 | m <- matrix(runif(9),3,3) 94 | v <- seq(1,100) 95 | R.matlab::writeMat("dir_convert2RData_1.mat", m=m) 96 | R.matlab::writeMat("dir_convert2RData_2.mat", v=v) 97 | convert2RData() 98 | bool <- suppressWarnings(file.remove(c("dir_convert2RData_1.mat", "dir_convert2RData_2.mat", 99 | "dir_convert2RData_1.Rdata", "dir_convert2RData_2.Rdata"))) 100 | expect_equal(bool, c(TRUE, TRUE, TRUE, TRUE)) 101 | 102 | 103 | 104 | #### conversion of a single specified .mat file in a specified directory #### 105 | this_dir <- getwd() 106 | v <- seq(1,10) 107 | R.matlab::writeMat("file_dir_convert2RData.mat", v=v) 108 | convert2RData("file_dir_convert2RData.mat", this_dir) 109 | bool <- suppressWarnings(file.remove(c("file_dir_convert2RData.mat", "file_dir_convert2RData.RData"))) 110 | expect_equal(bool, c(TRUE, TRUE)) 111 | 112 | 113 | 114 | #### conversion of several specified .mat files in the current working directory #### 115 | v <- sample(1:10,4) 116 | m <- matrix(runif(9),3,3) 117 | R.matlab::writeMat("twofiles_convert2RData_1.mat", v=v) 118 | R.matlab::writeMat("twofiles_convert2RData_2.mat", m=m) 119 | convert2RData(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat")) 120 | bool <- suppressWarnings(file.remove(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat", 121 | "twofiles_convert2RData_1.RData", "twofiles_convert2RData_2.RData"))) 122 | expect_equal(bool, c(TRUE, TRUE, TRUE, TRUE)) 123 | }) 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | test_that("a wrong directory input results in an error", { 133 | this_dir <- paste(getwd(), "/nonsenseDir2", sep = "") 134 | expect_error(convert2RData(dir = this_dir), regexp = "Wrong input argument dir. Does not exist or is no directory.") 135 | }) 136 | -------------------------------------------------------------------------------- /reach/R/convert2RData.R: -------------------------------------------------------------------------------- 1 | #' Converts a Matlab data file (.mat) to R data file (.RData) 2 | #' 3 | #' Converts all or specified Matlab .mat files in the specified directory to 4 | #' .RData files, while keeping the .mat files unchanged. The Matlab files must 5 | #' have been saved in a MAT file format version supported by R.matlab's 6 | #' \code{readMat} function (e.g. saved with the -v7 option flag, but not with 7 | #' the -v7.3 flag). 8 | #' 9 | #' @param matfile character or character vector denoting one or several 10 | #' matfiles, e.g. "mymatlabdata", "mymatlabdata.mat", c("one.mat", "two.mat"). 11 | #' Providing the file type specifier ".mat" is optional. Defaults to NULL, 12 | #' which means that all .mat files in the specified directory \code{dir} will 13 | #' be converted. 14 | #' 15 | #' @param dir path to a directory which contains one or several .mat files that 16 | #' should be converted to .RData files. Defaults to the current working 17 | #' directory. 18 | #' 19 | #' @param verbose logical indicating whether console output about the conversion 20 | #' process should be printed 21 | #' 22 | #' @export 23 | #' 24 | #' @examples 25 | #' \dontrun{ 26 | #' 27 | #' ##### conversion: specified .mat file in current working directory #### 28 | #' v <- sample(1:10,4) 29 | #' m <- matrix(runif(9),3,3) 30 | #' R.matlab::writeMat("file_convert2RData.mat", v=v, m=m) 31 | #' rm(v,m) 32 | #' convert2RData("file_convert2RData.mat") 33 | #' load("file_convert2RData.RData") 34 | #' print(v) 35 | #' print(m) 36 | #' file.remove(c("file_convert2RData.RData", "file_convert2RData.mat")) 37 | #' 38 | #' 39 | #' 40 | #' #### conversion: all .mat files in a specified directory #### 41 | #' this_dir <- getwd() 42 | #' m <- matrix(runif(9),3,3) 43 | #' v <- seq(1,100) 44 | #' R.matlab::writeMat("dir_convert2RData_1.mat", m=m) 45 | #' R.matlab::writeMat("dir_convert2RData_2.mat", v=v) 46 | #' rm(v,m) 47 | #' convert2RData(dir = this_dir, verbose = TRUE) 48 | #' load(paste(this_dir, "/dir_convert2RData_1.Rdata", sep = "")) 49 | #' print(m) 50 | #' load(paste(this_dir, "/dir_convert2RData_2.Rdata", sep = "")) 51 | #' print(v) 52 | #' file.remove(c(paste(this_dir, "/dir_convert2RData_1.mat", sep = ""), 53 | #' paste(this_dir, "/dir_convert2RData_2.mat", sep = ""), 54 | #' paste(this_dir, "/dir_convert2RData_1.Rdata", sep = ""), 55 | #' paste(this_dir, "/dir_convert2RData_2.Rdata", sep = ""))) 56 | #' 57 | #' 58 | #' 59 | #' #### conversion: specified .mat file in a specified directory #### 60 | #' this_dir <- getwd() 61 | #' v <- seq(1,10) 62 | #' R.matlab::writeMat("file_dir_convert2RData.mat", v=v) 63 | #' rm(v) 64 | #' convert2RData("file_dir_convert2RData.mat", this_dir) 65 | #' load("file_dir_convert2RData.RData") 66 | #' print(v) 67 | #' file.remove(c("file_dir_convert2RData.mat", "file_dir_convert2RData.RData")) 68 | #' 69 | #' 70 | #' 71 | #' #### conversion: several specified .mat files in current working directory #### 72 | #' v <- sample(1:10,4) 73 | #' m <- matrix(runif(9),3,3) 74 | #' R.matlab::writeMat("twofiles_convert2RData_1.mat", v=v) 75 | #' R.matlab::writeMat("twofiles_convert2RData_2.mat", m=m) 76 | #' rm(v,m) 77 | #' convert2RData(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat")) 78 | #' load("twofiles_convert2RData_1.RData") 79 | #' print(v) 80 | #' load("twofiles_convert2RData_2.RData") 81 | #' print(m) 82 | #' file.remove(c("twofiles_convert2RData_1.mat", "twofiles_convert2RData_2.mat", 83 | #' "twofiles_convert2RData_1.RData", "twofiles_convert2RData_2.RData")) 84 | #' } 85 | #' 86 | #' @author Christoph Schmidt 87 | 88 | # 19.12.15 89 | 90 | 91 | convert2RData <- function(matfile = NULL, dir = "./", verbose = FALSE){ 92 | 93 | isDir <- file.info(dir)$isdir 94 | 95 | if(is.na(isDir) || !isDir){ 96 | stop("Wrong input argument dir. Does not exist or is no directory.") 97 | } 98 | 99 | 100 | 101 | if(dir != "./"){ 102 | old_wd <- getwd() 103 | setwd(dir) 104 | } 105 | 106 | 107 | 108 | if(is.null(matfile)){ # convert all .mat files in dir 109 | dirCont <- list.files(pattern="*.mat$") 110 | 111 | if(length(dirCont)==0){ 112 | if(dir != "./"){ 113 | setwd(old_wd) 114 | } 115 | stop("No .mat files found in specified directory.") 116 | } 117 | 118 | 119 | for(f in 1:length(dirCont)){ 120 | if(verbose){ 121 | writeLines("") 122 | writeLines(paste("Conversion file #", f, " - ", dirCont[f])) 123 | } 124 | convertFile(dirCont[f], verbose) 125 | } 126 | } 127 | else { # convert specific .mat files in dir 128 | for(f in 1:length(matfile)){ 129 | matfile_ <- check_matfile_format(matfile[f]) 130 | 131 | if(is.na(file.info(matfile_)$size)){ 132 | if(dir != "./"){ 133 | setwd(old_wd) 134 | } 135 | stop(paste("Specified matfile #: ", f, "\n\t", matfile[f], "\ndoes not exist in specified directory.", sep = "")) 136 | } 137 | 138 | convertFile(matfile_, verbose) 139 | } 140 | 141 | } 142 | 143 | 144 | if(dir != "./"){ 145 | setwd(old_wd) 146 | } 147 | } 148 | 149 | 150 | 151 | 152 | 153 | # Converts a .mat file to a .RData file 154 | convertFile <- function(fname, verbose){ # fname ends with .mat 155 | if(verbose){ 156 | writeLines(paste("Loading...", fname)) 157 | } 158 | 159 | matCont <- R.matlab::readMat(fname) 160 | 161 | 162 | for(k in 1:length(matCont)){ 163 | assign(names(matCont)[k], matCont[[names(matCont)[k]]]) 164 | } 165 | 166 | 167 | save(list=names(matCont), file = paste(stringr::str_sub(fname, 1, nchar(fname)-4), ".RData", sep="")) 168 | 169 | if(verbose){ 170 | writeLines("Finished saving corresponding .RData file.") 171 | } 172 | } 173 | 174 | 175 | 176 | 177 | 178 | 179 | # Checks whether the .mat file name ends with '.mat' and if necessary appends this suffix to the file name 180 | check_matfile_format <- function(fname){ 181 | if(!stringr::str_detect(stringr::str_sub(fname, -4L, -1L), ".mat")){ 182 | fname <- paste(fname, ".mat", sep = "") # works even if fname ends with '.', or contains '.mat' somewhere different from its end 183 | } 184 | 185 | return(fname) 186 | } 187 | -------------------------------------------------------------------------------- /reach/tests/testthat/test_runMatlabFct.R: -------------------------------------------------------------------------------- 1 | library(reach) 2 | library(testthat) 3 | 4 | context("Evaluating Matlab functions within R") 5 | 6 | test_that("runMatlabFct terminates", { 7 | testthat::skip_on_travis() # since Travis CI has no MATLAB installed tests would fail there 8 | 9 | 10 | a <- c(1,2,1,4,1,5,4,3,2,2,1,6,3,1,8,3,5,5) 11 | b <- c(4,6,9,8) 12 | fcall <- "[bool, pos] = ismember(b,a)" 13 | expect_that(runMatlabFct(fcall), not(throws_error())) 14 | 15 | 16 | 17 | M <- matrix(c(2,-1,0, -1,2,-1, 0,2,3), 3, 3) 18 | fcall <- "C = chol(M)" 19 | expect_that(runMatlabFct(fcall), not(throws_error())) 20 | 21 | 22 | 23 | expect_that(runMatlabFct("h=image()"), not(throws_error())) 24 | 25 | 26 | 27 | x <- seq(1, 2.5*pi, 0.01) 28 | y <- (sin(x))^4 29 | expect_that(runMatlabFct("h = plot( x , y )"), not(throws_error())) 30 | 31 | 32 | 33 | expect_that(runMatlabFct("A=rand(16)"), not(throws_error())) 34 | 35 | 36 | 37 | orig_str <- 'this_test_was_my_first_test' 38 | old_sub <- 'test' 39 | new_sub <- 'assignment' 40 | expect_that(runMatlabFct('str=strrep(orig_str, old_sub, new_sub)'), not(throws_error())) 41 | 42 | 43 | 44 | expect_that(out <- runMatlabFct("out=matlab_test_fct_1(11, 22, 33, 44, 55, 66, 777, 8888)"), not(throws_error())) 45 | 46 | 47 | 48 | expect_that(out <- runMatlabFct("[a,bbb,c_,d-,e_e,f,gG]=matlab_test_fct_2(3)"), not(throws_error())) 49 | 50 | 51 | 52 | val_ <- -9 53 | expect_that(out <- runMatlabFct("[a,b,c,d,e,f,g]=matlab_test_fct_3(2, -3, 44, 5, val_)"), not(throws_error())) 54 | 55 | 56 | 57 | val_ <- -9 58 | expect_that(out <- runMatlabFct("[a,b]=matlab_test_fct_3(-3, val_)"), not(throws_error())) 59 | 60 | 61 | 62 | val_ <- -9 63 | expect_that(out <- runMatlabFct("[a,b,c]=matlab_test_fct_3(-3, val_, 4)"), not(throws_error())) 64 | 65 | 66 | 67 | 68 | 69 | val2 <- 12 70 | expect_that(out <- runMatlabFct("[a,b,c]=matlab_test_fct_3(val_, 4, val2)"), not(throws_error())) 71 | }) 72 | 73 | 74 | 75 | 76 | test_that("runMatlabFct returns Matlab results correctly", { 77 | testthat::skip_on_travis() # since Travis CI has no MATLAB installed tests would fail there 78 | 79 | 80 | a <- c(1,2,1,4,1,5,4,3,2,2,1,6,3,1,8,3,5,5) 81 | b <- c(4,6,9,8) 82 | fcall <- "[bool, pos] = ismember(b,a)" 83 | results <- runMatlabFct(fcall) 84 | expect_true(all(results$bool == c(TRUE, TRUE, FALSE, TRUE))) 85 | expect_true(all(results$pos == c(4, 12, 0, 15))) 86 | 87 | 88 | 89 | M <- matrix(c(2,-1,0, -1,2,-1, 0,2,3), 3, 3) 90 | fcall <- "C = chol(M)" 91 | results <- runMatlabFct(fcall) 92 | C <- results$C 93 | C2 <- structure(c(1.4142135623731, 0, 0, -0.707106781186547, 1.22474487139159, 94 | 0, 0, 1.63299316185545, 0.577350269189625), .Dim = c(3L, 3L)) 95 | expect_true( all(round(C, 8) == round(C2, 8)) ) # from dput 96 | }) 97 | 98 | 99 | 100 | 101 | 102 | test_that("an error is thrown if input is not present in the environment the function was called from (parent frame)", { 103 | testthat::skip_on_travis() 104 | 105 | nu <- 0.5 106 | expect_error(runMatlabFct("v = bessely(nu, vec)"), 107 | regexp = "Matlab input argument (given in fcall) does not exist: vec", 108 | fixed = TRUE) 109 | }) 110 | 111 | 112 | 113 | 114 | 115 | test_that("result is not null for input with special symbols", { 116 | testthat::skip_on_travis() 117 | 118 | A <- matrix(runif(16), 4, 4) 119 | expect_that(res <- runMatlabFct("A_=inv(A)"), not(throws_error())) 120 | expect_false(is.null(res$A_)) 121 | 122 | 123 | 124 | nu_ <- 0.32 125 | vec_ <- c(1,2,5,2,6) 126 | expect_that(res_ <- runMatlabFct("v_ = bessely(nu_, vec_)"), not(throws_error())) 127 | expect_false(is.null(res_$v_)) 128 | }) 129 | 130 | 131 | 132 | 133 | 134 | # # should be commented out because plot window/Matlab has to be terminated manually when no 135 | # # Matlab output is specified in the argument to runMatalbFct(): 136 | # 137 | # test_that("function works when no output was specified", { 138 | # testthat::skip_on_travis() 139 | # 140 | # x <- seq(1, 2.5*pi, 0.01) 141 | # y <- (sin(x))^4 142 | # expect_that(runMatlabFct("plot( x , y )"), not(throws_error())) 143 | # }) 144 | 145 | 146 | 147 | 148 | 149 | 150 | # # should be commented out because R package una and the pairwiseVertexSimilarity() 151 | # # function are not public: 152 | # 153 | test_that("temporary files are deleted automatically", { 154 | testthat::skip_on_travis() 155 | 156 | A <- una::randDir(7, p = 0.23) 157 | 158 | D2 <- reach::runMatlabFct("D2 = pairwiseVertexSimilarity(A, 1)")$D2 159 | expect_false(file.exists("tmp_1.mat")) 160 | 161 | method <- 1 162 | D2 <- reach::runMatlabFct("D2 = pairwiseVertexSimilarity(A, method)")$D2 163 | expect_false(file.exists("tmp_1.mat")) 164 | 165 | 166 | 167 | M <- reach::runMatlabFct("M = magic(7)") 168 | expect_false(file.exists("tmp_1.mat")) 169 | 170 | 171 | 172 | A <- matrix(sample(1,100,25), 5, 5) 173 | B <- matrix(runif(100), 10, 10) 174 | expect_that(out <- runMatlabFct("out=matlab_test_fct_1(11, 22, A, 44, 55, 66, 777, B)"), not(throws_error())) 175 | expect_false(file.exists("tmp_1.mat")) 176 | expect_that(out <- runMatlabFct("out=matlab_test_fct_1(1, A)"), not(throws_error())) 177 | expect_false(file.exists("tmp_1.mat")) 178 | }) 179 | 180 | 181 | 182 | 183 | 184 | 185 | test_that("handling input arguments that have writeMat() option names (not written to tmp_1.mat)", { 186 | testthat::skip_on_travis() 187 | 188 | expect_error(out <- runMatlabFct("out=matlab_test_fct_1(11, 22, 33, 44, 55, 66, con, 8888)"), 189 | regexp = "Input arguments have names incompatible with internal call to the 'writeMat()' function.", fixed=TRUE) 190 | 191 | expect_error(out <- runMatlabFct("out=matlab_test_fct_1(11, verbose, 33, 44, 55, 66, 1, 8888)"), 192 | regexp = "Input arguments have names incompatible with internal call to the 'writeMat()' function.", fixed=TRUE) 193 | 194 | expect_error(out <- runMatlabFct("out=matlab_test_fct_1(verbose, 33, onWrite)"), 195 | regexp = "Input arguments have names incompatible with internal call to the 'writeMat()' function.", fixed=TRUE) 196 | 197 | expect_error(out <- runMatlabFct("out=matlab_test_fct_1(onWrite)"), 198 | regexp = "Input arguments have names incompatible with internal call to the 'writeMat()' function.", fixed=TRUE) 199 | 200 | expect_error(out <- runMatlabFct("out=matlab_test_fct_1(matVersion)"), 201 | regexp = "Input arguments have names incompatible with internal call to the 'writeMat()' function.", fixed=TRUE) 202 | 203 | expect_error(out <- runMatlabFct("out=matlab_test_fct_1(8, matVersion, 10, onWrite, con, 11, verbose)"), 204 | regexp = "Input arguments have names incompatible with internal call to the 'writeMat()' function.", fixed=TRUE) 205 | }) 206 | -------------------------------------------------------------------------------- /reach/R/runMatlabFct.R: -------------------------------------------------------------------------------- 1 | #' Runs a Matlab function like an R function and returns its results 2 | #' 3 | #' Runs Matlab on the R console, evaluates the specified Matlab function with 4 | #' given arguments and directly returns the Matlab output as a single value or a 5 | #' list of values back to the R session. The function acts as a proxy for the 6 | #' specified Matlab function. It handles all Matlab related things internally 7 | #' and transparently to the user and just returns the specified Matlab output 8 | #' arguments as results of the computations performed by Matlab. 9 | #' 10 | #' @note For starting several Matlab functions independently of each other, or a 11 | #' chain of Matlab functions, consider using the 'runMatlabCommand()' 12 | #' function, or writing a Matlab script file and use the 'runMatlabScript()' 13 | #' function and store the results in a .mat file that can be read back into R 14 | #' with the help of the 'convert2RData()' function. 15 | #' 16 | #' @param fcall string specifying the Matlab function call with output and input 17 | #' parameters, just as one would call the function inside a regular Matlab 18 | #' session. The input arguments specified in fcall must be variable names 19 | #' stored in the current R session (the environment where the function was 20 | #' called from) or numeric values. Variable names are preferred and should be 21 | #' the standard for using this function. If no output parameter are contained 22 | #' in fcall, then it is assumed that the user expects Matlab to not 23 | #' automatically quit (e.g. because the function call was a plotting function 24 | #' and the plot window should stay open). In this case the Matlab process has 25 | #' to be terminated manually (!) by the user before the function can terminate 26 | #' and one can continue to work in the R session. Nested Matlab function calls 27 | #' (function as input argument for a function) are currently not supported. 28 | #' 29 | #' @return The results of the Matlab function call. A list of named entries that 30 | #' correspond to the output arguments of the Matlab function as specified in 31 | #' \emph{fcall}. 32 | #' 33 | #' @details This function calls the user specified Matlab function and manages 34 | #' the necessary data exchange transparently, thus providing a seamless 35 | #' experience. The data is exchanged robustly over the file system, using 36 | #' temporary files that will be deleted automatically. 37 | #' 38 | #' @seealso \code{\link{runMatlabScript}}, \code{\link{runMatlabCommand}} 39 | #' 40 | #' @export 41 | #' 42 | #' @examples 43 | #' \dontrun{ 44 | #' a <- c(1,2,1,4,1,5,4,3,2,2,1,6,3,1,3,5,5) 45 | #' b <- c(4,6,9) 46 | #' fcall <- "[bool, pos] = ismember(b,a)" 47 | #' 48 | #' results <- runMatlabFct(fcall) 49 | #' 50 | #' bool <- results$bool 51 | #' pos <- results$pos 52 | #' print(a) 53 | #' print(b) 54 | #' print(fcall) 55 | #' print(bool) 56 | #' print(pos) 57 | #' 58 | #' 59 | #' 60 | #' # !the Matlab process has to be terminated manually! 61 | #' runMatlabFct("image") 62 | #' 63 | #' 64 | #' 65 | #' # !the Matlab process has to be terminated manually! 66 | #' # wrong Matlab function input ( it should be penny or penny() ), but corrected internally 67 | #' runMatlabFct("penny(") 68 | #' 69 | #' 70 | #' 71 | #' M <- matrix(c(2,-1,0, -1,2,-1, 0,2,3), 3, 3) 72 | #' fcall <- "C = chol(M)" 73 | #' results <- runMatlabFct(fcall) 74 | #' print(results) 75 | #' 76 | #' 77 | #' 78 | #' A <- runMatlabFct("A=rand(6)")$A 79 | #' A_ <- runMatlabFct("A_=inv(A)")$A_ 80 | #' print(round(A %*% A_)) 81 | #' 82 | #' 83 | #' 84 | #' nu_ <- 0.32 85 | #' vec_ <- c(1,2,5,2,6) 86 | #' res_ <- runMatlabFct("v_ = bessely(nu_, vec_)") 87 | #' print(res_$v_) 88 | #' 89 | #' 90 | #' 91 | #' orig_str <- 'this_test_was_my_first_test' 92 | #' old_sub <- 'test' 93 | #' new_sub <- 'assignment' 94 | #' new_str <- runMatlabFct('str=strrep(orig_str, old_sub, new_sub)') 95 | #' print(new_str$str) 96 | #' 97 | #' } 98 | #' 99 | #' @author Christoph Schmidt 100 | 101 | # 05.06.16 102 | 103 | runMatlabFct <- function(fcall){ 104 | ### Avoid problems when input has no parentheses--- 105 | if(!stringr::str_detect(fcall, "\\(" )){ 106 | fcall <- stringr::str_replace_all(fcall, "\\)", "") 107 | fcall <- stringr::str_c(fcall, "()") 108 | } 109 | 110 | 111 | if(!stringr::str_detect(fcall, "\\)" )){ 112 | fcall <- stringr::str_replace_all(fcall, "\\(", "") 113 | fcall <- stringr::str_c(fcall, "()") 114 | } 115 | 116 | 117 | 118 | 119 | 120 | 121 | ### Parsing fct call - extract input arguments (strings)--- 122 | ind1 <- stringr::str_locate(fcall, "\\(")[1,1] 123 | ind2 <- stringr::str_locate_all(fcall, "\\)")[[1]] # should be the last position in the string (or second last if at the last position is an ';') 124 | ind2 <- ind2[dim(ind2)[1], 1] 125 | fcall_ <- stringr::str_sub(fcall, ind1+1, ind2-1) 126 | inp <- str_extractCommaSepArgs(fcall_) 127 | 128 | 129 | 130 | 131 | 132 | 133 | # input args with the same name as writeMat() options, like 'verbose', cannot be written to tmp_1 .mat file 134 | if(any(c("verbose", "con", "matVersion", "onWrite") %in% inp)){ 135 | stop(paste("\nInput arguments have names incompatible with internal call to the 'writeMat()' function.\n", 136 | "'verbose, 'con', 'matVersion' and 'onWrite' are not allowed.", sep = "")) 137 | } 138 | 139 | 140 | 141 | 142 | 143 | 144 | ### Parsing fct call - extract output arguments--- 145 | if(stringr::str_detect(fcall, "\\[")){ 146 | ind1 <- stringr::str_locate_all(fcall, "\\[")[[1]] # should be the last position in the string (or second last if at the last position is an ';') 147 | ind1 <- ind1[dim(ind1)[1], 1] 148 | ind2 <- stringr::str_locate(fcall, "\\]")[1,1] 149 | 150 | } else { 151 | ind1 <- 0 152 | ind2 <- stringr::str_locate(fcall, "=")[1,1] 153 | } 154 | 155 | fcall_ <- stringr::str_sub(fcall, ind1+1, ind2-1) 156 | out <- str_extractCommaSepArgs(fcall_) 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | ### checking whether output arguments have to be changed/ substituted due to symbols incompatible with R.matlab readMat()--- 165 | if( is.na(out[[1]]) ){ out_present <- FALSE } else { out_present <- TRUE } 166 | 167 | 168 | if(out_present){ # if output args were specified 169 | out_orig <- out 170 | ind_repl <- 1 171 | pattern <- "[_-]" 172 | 173 | for(k in 1:length(out)){ 174 | thisarg <- out[[k]] 175 | 176 | if( stringr::str_detect(thisarg, pattern) ){ 177 | out[[k]] <- stringr::str_replace_all(thisarg, pattern, as.character(ind_repl)) 178 | fcall <- stringr::str_replace_all(fcall, out_orig[[k]], out[[k]]) 179 | ind_repl <- ind_repl + 1 180 | } 181 | } 182 | } 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | ### Check if input arguments are available in the environment where the function was called from--- 193 | itswin <- isWin() 194 | 195 | tmp_2 <- tempfile("rmf_", getwd(), ".mat") # Matlab returned data (computation) 196 | if(itswin){ tmp_2 <- stringr::str_replace_all(tmp_2, "\\\\", "/") } # adjusting path specificatin for Windows 197 | 198 | if(itswin){ 199 | tmp_3 <- tempfile("rmf_", getwd(), ".mat") # Matlab returned data (indicating computation data was completely written to disk) 200 | tmp_3 <- stringr::str_replace_all(tmp_3, "\\\\", "/") # adjusting path specificatin for Windows 201 | } 202 | 203 | 204 | if( !identical(inp[[1]], "") ){ inp_present <- TRUE } else { inp_present <- FALSE } 205 | 206 | if(inp_present){ # only if input is specified (and is a workspace variable name), the 1st temporary file tmp_1 is written & the Matlab command generated 207 | tmp_1 <- tempfile("rmf_", getwd(), ".mat") 208 | if(itswin){ tmp_1 <- stringr::str_replace_all(tmp_1, "\\\\", "/") } 209 | wM <- paste("matWrite(\"", tmp_1, "\", \"", sep = "") #writeMat() / matWrite() command 210 | any_var_in_envir <- FALSE 211 | 212 | for(k in 1:length(inp)){ 213 | if( exists(inp[[k]], envir = parent.frame()) ){ var_in_envir <- TRUE } else { var_in_envir <- FALSE } # parent.frame() returns the environment where the function was called from 214 | 215 | 216 | if( !is.na(suppressWarnings(as.double(inp[[k]]))) ){ var_is_num <- TRUE } else { var_is_num <- FALSE } # input was a numeric, e.g. "A=rand(16)" 217 | 218 | 219 | if( !var_in_envir && !var_is_num ){ 220 | stop(paste("Matlab input argument (given in fcall) does not exist:", inp[[k]])) 221 | } 222 | 223 | 224 | if( var_in_envir ){ 225 | any_var_in_envir <- TRUE 226 | assign( inp[[k]], get(inp[[k]], envir = parent.frame()) ) 227 | wM <- paste(wM, paste(inp[[k]], ", ", sep = "")) 228 | } 229 | } # for 1:length(inp) 230 | 231 | 232 | if( any_var_in_envir ){ 233 | wM <- stringr::str_sub(wM, end = -3L) 234 | wM <- paste(wM, "\")", sep = "") 235 | eval(parse(text=wM)) # writing tmp_1 236 | 237 | if(itswin){ 238 | thisc <- paste("load ", tmp_1, "; ", fcall, "; save('", tmp_2, "', '-v7'); wasSaved = true; save('", tmp_3, "', 'wasSaved', '-v7')", sep = "") # Matlab command 239 | } else { # OS X, Linux 240 | thisc <- paste("load ", tmp_1, "; ", fcall, "; save('", tmp_2, "', '-v7')", sep = "") # Matlab command 241 | } 242 | } 243 | } # inp_present 244 | 245 | 246 | if( !inp_present || !any_var_in_envir) { # input is not specified or is a numeric value (not a variable name), so no tmp_1 .mat file was written, hence it cannot be loaded 247 | if(itswin){ 248 | thisc <- paste(fcall, "; save('", tmp_2, "', '-v7'); wasSaved = true; save('", tmp_3, "', 'wasSaved', '-v7')", sep = "") # Matlab command 249 | } else { # OS X, Linux 250 | thisc <- paste(fcall, "; save('", tmp_2, "', '-v7')", sep = "") # Matlab command 251 | } 252 | } 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | ### call to Matlab function--- 262 | if(out_present){ # if output args are specified, then Matlab should quit, otherwise not 263 | reach::runMatlabCommand(thisc, verbose = FALSE, do_quit = TRUE) # Matlab quits automatically 264 | 265 | } else { 266 | reach::runMatlabCommand(thisc, verbose = FALSE, do_quit = FALSE) 267 | } 268 | 269 | 270 | 271 | 272 | if(itswin){ 273 | Sys.sleep(4) # for Windows only, wait until Matlab finished the computations - since Matlab process is separated from the running R session 274 | while(TRUE){ 275 | if(file.exists(tmp_3)){ # returns TRUE, even if a large .mat file is still written and cannot be loaded (loading yields error 'Unknown data type) 276 | break 277 | 278 | } else { 279 | Sys.sleep(1.5) 280 | } 281 | } 282 | } 283 | 284 | 285 | 286 | 287 | 288 | ### Read back the results of the computation, delete all temporary files, return results--- 289 | if(out_present){ 290 | res <- R.matlab::readMat(tmp_2) 291 | res <- res[unlist(out)] 292 | names(res) <- out_orig 293 | } else { 294 | res <- "(-:" 295 | } 296 | 297 | 298 | if( inp_present && any_var_in_envir ){ 299 | if(itswin){ 300 | file.remove(c(tmp_1, tmp_2, tmp_3)) 301 | 302 | } else { 303 | file.remove(c(tmp_1, tmp_2)) 304 | } 305 | 306 | } else { 307 | if(itswin){ 308 | file.remove(tmp_2, tmp_3) 309 | 310 | } else { 311 | file.remove(tmp_2) 312 | } 313 | } 314 | 315 | 316 | return(res) 317 | } 318 | --------------------------------------------------------------------------------