R copy text from function output with for loop to clipboard - r

I've written a little function that helps renaming columns by looping through each variable and pasting various punctuation around it so that text is sent to the console. This can then be copied into my script and rename variables as required. This is the function:
library(tidyverse)
tidy_rename <- function (df) {
df_name <- deparse(substitute(df))
names(df) <- tolower(names(df))
cat(paste(df_name, " <- ", paste(df_name, "%>%\n\t rename(")))
for (i in names(df)) {
cat(paste("\t\t", paste(paste("=", paste(paste('"', i), '"'))), ","), sep="\n")
}
writeLines(")"
)
}
If I use this on a dataset:
test_df <- data.frame("VarIable 1" = c(1), "sizrd" = c(1), "dat 1" = c(1),
"x-cord" = c(1), "y-crf" = c(1), "aGe" = c(1), check.names=F)
tidy_rename(test_df)
which gives the following which can be copied and pasted into script:
test_df <- test_df %>%
rename( = " variable 1 " ,
= " sizrd " ,
= " dat 1 " ,
= " x-cord " ,
= " y-crf " ,
= " age " ,
)
What I would like is to automatically copy this output to the clipboard within the function. I'm not sure how to use writeClipboard around the forloop. This doesn't work:
tidy_rename <- function (df) {
df_name <- deparse(substitute(df))
names(df) <- tolower(names(df))
writeClipboard(
cat(paste(df_name, " <- ", paste(df_name, "%>%\n\t rename(")))
for (i in names(df)) {
cat(paste("\t\t", paste(paste("=", paste(paste('"', i), '"'))), ","), sep="\n")
}
writeLines(")"
)
)
}
Any suggestions please?

Expanding on my comment to eliminate any confusion.
A method or suggestion is to store the string in a variable, which can then be output in the end. Note from the value of cat(...) is NULL (it doesnt return the string). This requires 2 variables, lets call them str and newstr. I'll let str store the entire string that you want to copy, and newstr store the current string that is output by cat(...).
tidy_rename <- function (df) {
df_name <- deparse(substitute(df))
names(df) <- tolower(names(df))
str <- paste(df_name, " <- ", paste(df_name, "%>%\n\t rename("))
cat(str)
for (i in names(df)) {
#Store variable at each iteration and expand str. Output newstr.
newstr <- paste("\t\t", paste(paste("=", paste(paste('"', i), '"'))), ",")
str <- paste(str, newstr, sep = "\n")
cat(newstr, sep="\n")
}
newstr <- ")"
str <- paste0(str, newstr)
cat(newstr)
writeClipboard(str)
}
Note how the output is stored in str at each iteration but newstr is output.
As a side note i suggest that OP checks out the collapse argument of paste (alternatively paste0). I don't have the full overview, but it seems like this could eliminate 2 - 3 calls to paste if the strings were collapsed within one of the function calls.

Related

R paste function repeats first argument for each word

Below is a code snippet written for writing the messages. But im not getting why the output prints the way below. Expected output is also given. I first thought the txt is a list type. But it is a character variable
writetext<-function(...){
arguments <- list(...)
if (length(arguments)>0){
txt<- paste(arguments)
if (length(txt)==0) return()
strtime <- format(Sys.time(),"%I:%M:%S%p")
txt <- paste(strtime,txt)
message(txt)
}
}
writetext("abc","efg")
01:05:13PM abc01:05:13PM efg
Expected :
01:05:13PM abcefg
You could use paste0(txt, collapse = "") :
writetext <- function(...) {
arguments <- list(...)
if (length(arguments) > 0) {
txt <- paste(arguments)
if (length(txt) == 0) return()
strtime <- format(Sys.time(), "%I:%M:%S%p")
txt <- paste(strtime, paste0(txt, collapse = ""))
message(txt)
}
}
writetext("abc", "efg")
# 07:13:45PM abcefg

function generated dataframe with colnames

I am trying to create a function that generates a data frame and add the colnames programmatically.
x is a vector
foo <- function(x){
dfoo <- data.frame(Mts = 1:12)
for (i in 1:length(x)){
dfoo[i + 1] <- 1:12*i
}
colnames(dfoo) <- c("Months", paste(x, "BAR" sep = " "))
return(dfoo)
}
but it is throwing this error Error: unexpected '}' in "}"
That is because you are missing a comma in this line inside the paste
function between "BAR" and sep:
c("Months", paste(x, "BAR" sep = " "))
It should be:
c("Months", paste(x, "BAR",sep = " "))

Remove path from variable name in a dataframe

I've put together a function that looks like this, with the first comment lines being an example. Most importantly here is the set.path variable that I use to set the path initially for the function.
# igor.import(set.path = "~/Desktop/Experiment1 Folder/SCNavigator/Traces",
# set.pattern = "StepsCrop.ibw",
# remove.na = TRUE)
igor.multifile.import <- function(set.path, set.pattern, remove.na){
{
require("IgorR")
require("reshape2")
raw_list <- list.files(path= set.path,
pattern= set.pattern,
recursive= TRUE,
full.names=TRUE)
multi.read <- function(f) { # Note that "temp.data" is just a placeholder in the function
temp_data <- as.vector(read.ibw(f)) # Change extension to match your data type
}
my_list <- sapply(X = raw_list, FUN = multi.read) # Takes all files gathered in raw_list and applies multi.read()
my_list_combined <- as.data.frame(do.call(rbind, my_list))
my_list_rotated <- t(my_list_combined[nrow(my_list_combined):1,]) # Matrix form
data_out <- melt(my_list_rotated) # "Long form", readable by ggplot2
data_out$frame <- gsub("V", "", data_out$Var1)
data_out$name <- gsub(set.path, "", data_out$Var2) # FIX THIS
}
if (remove.na == TRUE){
set_name <- na.omit(data_out)
} else if (remove.na == FALSE) {
set_name <- data_out
} else (set_name <- data_out)
}
When I run this function I'll get a large dataframe, where each file that matched the pattern will show up with a name like
/Users/Joh/Desktop/Experiment1 Folder/SCNavigator/Traces/Par994/StepsCrop.ibw`
that includes the entire filepath, and is a bit unwieldy to look at and deal with.
I've tried to remove the path part with the line that says
data_out$name <- gsub(set.path, "", data_out$Var2)
Similar to the command above that removes the dataframe auto-named V1, V2, V3... (which works). I can't remove the string part matching the set.path = "my/path/" though.
Regardless of what your set.path is, you can eliminate it by
gsub(".*/","",mypath)
mypath<-"/Users/Joh/Desktop/Experiment1 Folder/SCNavigator/Traces/Par994/StepsCrop.ibw"
gsub(".*/","",mypath)
[1] "StepsCrop.ibw"
`

Export R data.frame to SPSS

There is a package foreign with a function write.foreign() that can write a SPS and CSV file. The SPS file than can read the CSV fiel into SPSS including labels. Fine so far, but there are some issues with that function:
Newer SPSS versions may show an error that you have too few format definitions in DATA LIST
If there are "labels" for numeric variables stored via attr(), these are lost.
Even if the SPSS vesion supports strings up to 32767, the function write.foreign() stops if there are more than 255 in any variable.
Theres a star (*) if any character variables are used, but newer SPSS versions cannot handle that.
The CSV file is comma-separated and does (can) not use quotes, therefore no commas are allowed in strings (character)
Non-ASCII caracters (e.g. umlauts) will crash the import
Should you have a character that contains any NA value, you'll see...
... an error message like this:
Error in if (any(lengths > 255L)) stop("Cannot handle character variables longer than 255") :
missing value where TRUE/FALSE needed
I spent a lot of time with that and then found a good posting (http://r.789695.n4.nabble.com/SPSS-export-in-R-package-foreign-td921491.html) to start on and make it better. Here's my result, I'd like to share with you.
To export an R data.frame to SPSS, use write_sav from the haven package:
library(haven)
write_sav(mtcars, "mtcars.sav")
This function is a replacement for foreign:write.foreign to handle the issues stated above.
Note: To avoid issues with SPSS finding the CSV file, please specify the full path (!) at least for datafile (also if using the original foreign:write.foreign()).
Note: This script will replace a tabulator (TAB) and other spacing (incl. CR+LF) in strings by a blank without warning. One may consider using GET DATA instead of the troublesome DATA LIST to solve that limitation.
Note: There may be a warning In FUN(X[[i]], ...) : probable complete loss of accuracy in modulus - this refers to counting the decimals and can be ignored.
Note: POSIXlt and POSIXct variables are not yet handled by the script properly.
writeForeignMySPSS = function (df, datafile, codefile, varnames = NULL, len = 32767) {
adQuote <- function (x) paste("\"", x, "\"", sep = "")
# Last variable must not be empty for DATA LIST
if (any(is.na(df[[length(df)]]))) {
df$END_CASE = 0
}
# http://stackoverflow.com/questions/5173692/how-to-return-number-of-decimal-places-in-r
decimalplaces <- function(x) {
y = x[!is.na(x)]
if (length(y) == 0) {
return(0)
}
if (any((y %% 1) != 0)) {
info = strsplit(sub('0+$', '', as.character(y)), ".", fixed=TRUE)
info = info[sapply(info, FUN=length) == 2]
if (length(info) >= 2) {
dec = nchar(unlist(info))[seq(2, length(info), 2)]
} else {
return(0)
}
return(max(dec, na.rm=T))
} else {
return(0)
}
}
dfn <- lapply(df, function(x) if (is.factor(x))
as.numeric(x)
else x)
# Boolean variables (dummy coding)
bv = sapply(dfn, is.logical)
for (v in which(bv)) {
dfn[[v]] = ifelse(dfn[[v]], 1, 0)
}
varlabels <- names(df)
# Use comments where applicable
for (i in 1:length(df)) {
cm = comment(df[[i]])
if (is.character(cm) && (length(cm) > 0)) {
varlabels[i] = comment(df[[i]])
}
}
if (is.null(varnames)) {
varnames <- abbreviate(names(df), 8L)
if (any(sapply(varnames, nchar) > 8L))
stop("I cannot abbreviate the variable names to eight or fewer letters")
if (any(varnames != varlabels))
warning("some variable names were abbreviated")
}
varnames <- gsub("[^[:alnum:]_\\$##]", "\\.", varnames)
dl.varnames <- varnames
chv = sapply(df, is.character)
if (any(chv)) {
for (v in which(chv)) {
dfn[[v]] = gsub("\\s", " ", dfn[[v]])
}
lengths <- sapply(df[chv], function(v) max(nchar(v), na.rm=T))
if (any(lengths > len)) {
warning(paste("Clipped strings in", names(df[chv]), "to", len, "characters"))
for (v in which(chv)) {
df[[v]] = substr(df[[v]], start=1, stop=len)
}
}
lengths[is.infinite(lengths)] = 0
lengths[lengths < 1] = 1
lengths <- paste("(A", lengths, ")", sep = "")
# star <- ifelse(c(FALSE, diff(which(chv) > 1)), " *",
dl.varnames[chv] <- paste(dl.varnames[chv], lengths)
}
# decimals and bools
nmv = sapply(df, is.numeric)
dbv = sapply(df, is.numeric)
nv = (nmv | dbv)
decimals = sapply(df[nv], FUN=decimalplaces)
dl.varnames[nv] = paste(dl.varnames[nv], " (F", decimals+8, ".", decimals, ")", sep="")
if (length(bv) > 0) {
dl.varnames[bv] = paste(dl.varnames[bv], "(F1.0)")
}
rmv = !(chv | nv | bv)
if (length(rmv) > 0) {
dl.varnames[rmv] = paste(dl.varnames[rmv], "(F8.0)")
}
# Breaks in output
brv = seq(1, length(dl.varnames), 10)
dl.varnames[brv] = paste(dl.varnames[brv], "\n", sep=" ")
cat("SET LOCALE = ENGLISH.\n", file = codefile)
cat("DATA LIST FILE=", adQuote(datafile), " free (TAB)\n", file = codefile, append = TRUE)
cat("/", dl.varnames, " .\n\n", file = codefile, append = TRUE)
cat("VARIABLE LABELS\n", file = codefile, append = TRUE)
cat(paste(varnames, adQuote(varlabels), "\n"), ".\n", file = codefile,
append = TRUE)
factors <- sapply(df, is.factor)
if (any(factors)) {
cat("\nVALUE LABELS\n", file = codefile, append = TRUE)
for (v in which(factors)) {
cat("/\n", file = codefile, append = TRUE)
cat(varnames[v], " \n", file = codefile, append = TRUE)
levs <- levels(df[[v]])
cat(paste(1:length(levs), adQuote(levs), "\n", sep = " "),
file = codefile, append = TRUE)
}
cat(".\n", file = codefile, append = TRUE)
}
# Labels stored in attr()
attribs <- !unlist(lapply(sapply(df, FUN=attr, which="1"), FUN=is.null))
if (any(attribs)) {
cat("\nVALUE LABELS\n", file = codefile, append = TRUE)
for (v in which(attribs)) {
cat("/\n", file = codefile, append = TRUE)
cat(varnames[v], " \n", file = codefile, append = TRUE)
# Check labeled values
tc = list()
for (tcv in dimnames(table(df[[v]]))[[1]]) {
if (!is.null(tcl <- attr(df[[v]], tcv))) {
tc[tcv] = tcl
}
}
cat(paste(names(tc), tc, "\n", sep = " "),
file = codefile, append = TRUE)
}
cat(".\n", file = codefile, append = TRUE)
}
ordinal <- sapply(df, is.ordered)
if (any(ordinal)) {
tmp = varnames[ordinal]
brv = seq(1, length(tmp), 10)
tmp[brv] = paste(tmp[brv], "\n")
cat(paste("\nVARIABLE LEVEL", paste(tmp, collapse=" "), "(ORDINAL).\n"),
file = codefile, append = TRUE)
}
num <- sapply(df, is.numeric)
if (any(num)) {
tmp = varnames[num]
brv = seq(1, length(tmp), 10)
tmp[brv] = paste(tmp[brv], "\n")
cat(paste("\nVARIABLE LEVEL", paste(tmp, collapse=" "), "(SCALE).\n"),
file = codefile, append = TRUE)
}
cat("\nEXECUTE.\n", file = codefile, append = TRUE)
write.table(dfn, file = datafile, row = FALSE, col = FALSE,
sep = "\t", quote = F, na = "", eol = "\n", fileEncoding="UTF-8")
}
On the long term, the changes might be considered to be merged into the foreignpackage. Unfortunately, the bug reporting system for the r-project is currently limited to previously registered developers.
The SPSS extension command STATS GET R can read a data frame directly into an SPSS dataset from a saved R workspace. If this extension command is not already installed (it will show up on the File menu), it can be installed from the Utilities menu (Statistics 22-23) or the Extensions menu (Statistics 24+).
What I've found is that:
'foreign' can't handle values with commas in them (basically, it fails what Excel can do with .csv files).
'haven' demands SPSS-compatible names in the r data set.
What I did for the latter is rename them as haven found them.
It seemed to work in blocks:
'Initial 1' through 'Initial 4'
'Final 1' through 'Final 4'
'relapse' and 'Relapse'.

R Paste multiple

I am currently taking in multiple command line parameters within my R script such as :
args<-commandArgs(TRUE)
arg1 <- as.numeric(args[1])
arg2 <- as.numeric(args[2])
I am wanting to use these args within my paste string like below. My problem is that I can only figure out how to use 1 of the arguments and not both (arg1, arg2). Instead of "xxx" that I show below in my where clause (i.e. "columnname1 in (xxx)") how do I use the "arg1" command line parameter in place of "xxx"? I've tried a number of different ways and for some reason I can't figure it out. Should I concatenate two different strings to accomplish this or is there an easier way?
SQL<-paste(
"SELECT
*
FROM
table
WHERE
columnname1 in (xxx)
and
columnname2 in ('",arg2,"')",sep = "")
Thanks for your help!
Try:
SQL<-paste(
"SELECT
*
FROM
table
WHERE
columnname1 in ('",arg1,"')
and
columnname2 in ('",arg2,"')",sep = "", collapse="")
You could also use the following helper function that allows named substitutions:
SQL<-strsubst(
"SELECT * FROM table WHERE
columnname1 in ('$(arg1)') and
columnname2 in ('$(arg2)')",
list(arg1=arg1, arg2=arg2)
)
where strsubst is defined as follows:
strsubst <- function (template, map, verbose = getOption("verbose"))
{
pat <- "\\$\\([^\\)]+\\)"
res <- template
map <- unlist(map)
m <- gregexpr(pat, template)
idx <- which(sapply(m, function(x) x[[1]] != -1))
for (i in idx) {
line <- template[[i]]
if (verbose)
cat("input: |", template[[i]], "|\n")
starts <- m[[i]]
ml <- attr(m[[i]], "match.length")
sym <- substring(line, starts + 2, starts + ml - 2)
if (verbose)
cat("sym: |", sym, "|\n")
repl <- map[sym]
idx1 <- is.na(repl)
if (sum(idx1) > 0) {
warning("Don't know how to replace '", paste(sym[idx1],
collapse = "', '"), "'.")
repl[idx1] <- paste("$(", sym[idx1], ")", sep = "")
}
norepl <- substring(line, c(1, starts + ml), c(starts -
1, nchar(line)))
res[[i]] <- paste(norepl, c(repl, ""), sep = "", collapse = "")
if (verbose)
cat("output: |", res[[i]], "|\n")
}
return(res)
}

Resources