I receive a list test that may contain or miss a certain name variable.
When I retrieve items by name, e.g. temp = test[[name]] in case name is missing I temp is NULL. In other cases, temp has inadequate value, so I want to throw a warning, something like name value XXX is invalid, where XXX is temp (I use sprintf for that purpose) and assign the default value.
However, I have a hard time converting it to string. Is there one-liner in R to do this?
as.character produces character(0) which turns the whole sprintf argument to character(0).
Workflow typically looks like:
for (name in name_list){
temp = test[[name]]
if(is.null(temp) || is_invalid(temp) {
warning(sprintf('%s is invalid parameter value for %s', as.character(temp), name))
result = assign_default(name)
} else {
result = temp
print(sprintf('parameter %s is OK', name)
}
}
PS.
is_invalid is function defined elsewhere. I need subsitute of as.character that would return '' or 'NULL'.
test = list(t1 = "a", t2 = NULL, t3 = "b")
foo = function(x){
ifelse(is.null(test[[x]]), paste(x, "is not valid"), test[[x]])
}
foo("t1")
#[1] "a"
foo("t2")
#[1] "t2 is not valid"
foo("r")
#[1] "r is not valid"
You can use format() to convert NULL to "NULL".
In your example it would be:
warning(sprintf('%s is invalid parameter value for %s', format(temp), name))
Well, as ultimately my goal was to join two strings, one of which might be empty (null), I realized, I just can use paste(temp, "name is empty or invalid") as my warning string. It doesn't exactly convert NULL to the string, but it's a solution.
Related
I am to construct a function named read_text_file.
It takes in an argument textFilePath that is a single character and two optional parameters withBlanks and withComments that are both single
logicals;
textFilePath is the path to the text file (or R script);
if withBlanks and withComments are set to FALSE, then read_text_file() will return the text file without blank lines (i.e. lines that contain nothing or only whitespace) and commented (i.e. lines that starts with “#”) lines respectively;
it outputs a character vector of length n where each element corresponds to its respective line of text/code.
I came up with the function below:
read_text_file <- function(textFilePath, withBlanks = TRUE, withComments = TRUE){
# check that `textFilePath`: character(1)
if(!is.character(textFilePath) | length(textFilePath) != 1){
stop("`textFilePath` must be a character of length 1.")}
if(withComments==FALSE){
return(grep('^$', readLines(textFilePath),invert = TRUE, value = TRUE))
}
if(withBlanks==FALSE){
return(grep('^#', readLines(textFilePath),invert = TRUE, value = TRUE))
}
return(readLines(textFilePath))
}
The second if-statement will always be executed leaving the third if-statement unexecuted.
I'd recommend processing an imported object instead of returning it immediately:
read_text_file <- function(textFilePath, withBlanks = TRUE, withComments = TRUE){
# check that `textFilePath`: character(1)
if(!is.character(textFilePath) | length(textFilePath) != 1){
stop("`textFilePath` must be a character of length 1.")}
result = readLines(textFilePath)
if(!withComments){
result = grep('^\\s*#\\s*', result, invert = TRUE, value = TRUE)
}
if(!withBlanks){
result = grep('^\\s*$', result, invert = TRUE, value = TRUE)
}
result
}
The big change is defining the result object that we modify as needed and then return at the end. This is good both because (a) it is more concise, not repeating the readLines command multiple times, (b) it lets you easily do 0, 1, or more data cleaning steps on result before returning it.
I also made some minor changes:
I don't use return() - it is only needed if you are returning something before the end of the function code, which with these modifications is not necessary.
You had your "comment" and "blank" regex patterns switched, I corrected that.
I changed == FALSE to !, which is a little safer and good practice. You could use isFALSE() if you want more readability.
I added \\s* into your regex patterns in a couple places which will match any amount of whitespace (including none)
I want to use quoted arguments in my function and I would like to allow the user to specify that they don't want to use the argument by setting it to NULL. However, rlang::ensym throws an error when it receives a NULL argument. Here is my code:
f <- function(var){
rlang::ensym(var)
return(var + 2)
}
# This works
variable = 2
f(variable)
# This throws an error
f(NULL)
The error message is:
Error: Only strings can be converted to symbols
I already tried adding an if-clause with is.null(var) before the expression with rlang::ensym, but of course, this doesn't work as the variable is not yet quoted at this time.
How can I check that the supplied quoted variable is NULL in order to handle it differently?
If you need to allow for NULL, it's more robust to use quosures first. Then you can inspect the quosure to see what's inside. For example
f <- function(var){
var <- rlang::enquo(var)
if (rlang::quo_is_null(var)) {
var <- NULL
} else if (rlang::quo_is_symbol(var)) {
var <- rlang::get_expr(var)
} else {
stop(paste("Expected symbol but found", class(rlang::get_expr(var))))
}
return(var)
}
And that returns
f(variable)
# variable
f(NULL)
# NULL
f(x+1)
# Error in f(x + 1) : Expected symbol but found call
Or you can use whatever logic is appropriate for your actual requirements.
I got a string (str1) and I want to extract anything after pattern "mycode=",
local str1 = "ServerName/codebase/?mycode=ABC123";
local tmp1 = string.match(str1, "mycode=%w+");
local tmp2 = string.gsub(tmp1,"mycode=", "");
From the logs,
tmp1 => mycode=ABC123
tmp2 => ABC123
Is there a better/more efficient way to do this? I do belive lua strings do not follow the POSIX standard (due to the size of the code base).
Yes, use a capture in your pattern to control what you get back from string.match.
From the lua reference manual (emphasis mine):
Looks for the first match of pattern in the string s. If it finds one, then match returns the captures from the pattern; otherwise it returns nil. If pattern specifies no captures, then the whole match is returned. A third, optional numerical argument init specifies where to start the search; its default value is 1 and can be negative.
It works like this:
> local str1 = "ServerName/codebase/?mycode=ABC123"
> local tmp1 = string.match(str1, "mycode=%w+")
> print(tmp1)
mycode=ABC123
> local tmp2 = string.match(str1, "mycode=(%w+)")
> print(tmp2)
ABC123
Say I have a string.
"poop"
I want to change "poop" to "peep".
In fact, I also want all of the o's in poop to change to e's for any word I put in.
Here's my attempt to do the above.
def getword():
x = (input("Please enter a word."))
return x
def main():
y = getword()
for i in range (len(y)):
if y[i] == "o":
y = y[:i] + "e"
print (y)
main()
As you can see, when you run it, it doesn't amount to what I want. Here is my expected output.
Enter a word.
>>> brother
brether
Something like this. I need to do it using slicing. I just don't know how.
Please keep your answer simple, since I'm somewhat new to Python. Thanks!
This uses slicing (but keep in mind that slicing is not the best way to do it):
def f(s):
for x in range(len(s)):
if s[x] == 'o':
s = s[:x]+'e'+s[x+1:]
return s
Strings in python are non-mutable, which means that you can't just swap out letters in a string, you would need to create a whole new string and concatenate letters on one-by-one
def getword():
x = (input("Please enter a word."))
return x
def main():
y = getword()
output = ''
for i in range(len(y)):
if y[i] == "o":
output = output + 'e'
else:
output = output + y[i]
print(output)
main()
I'll help you this once, but you should know that stack overflow is not a homework help site. You should be figuring these things out on your own to get the full educational experience.
EDIT
Using slicing, I suppose you could do:
def getword():
x = (input("Please enter a word."))
return x
def main():
y = getword()
output = '' # String variable to hold the output string. Starts empty
slice_start = 0 # Keeps track of what we have already added to the output. Starts at 0
for i in range(len(y) - 1): # Scan through all but the last character
if y[i] == "o": # If character is 'o'
output = output + y[slice_start:i] + 'e' # then add all the previous characters to the output string, and an e character to replace the o
slice_start = i + 1 # Increment the index to start the slice at to be the letter immediately after the 'o'
output = output + y[slice_start:-1] # Add the rest of the characters to output string from the last occurrence of an 'o' to the end of the string
if y[-1] == 'o': # We still haven't checked the last character, so check if its an 'o'
output = output + 'e' # If it is, add an 'e' instead to output
else:
output = output + y[-1] # Otherwise just add the character as-is
print(output)
main()
Comments should explain what is going on. I'm not sure if this is the most efficient or best way to do it (which really shouldn't matter, since slicing is a terribly inefficient way to do this anyways), just the first thing I hacked together that uses slicing.
EDIT Yeah... Ourous's solution is much more elegant
Can slicing even be used in this situation??
The only probable solution I think would work, as MirekE stated, is y.replace("o","e").
The code given below is to convert binary files from float32 to 16b with scale factor of 10. I am getting error of invalidation of %d.
setwd("C:\\2001")
for (b in paste("data", 1:365, ".flt", sep="")) {
conne <- file(b, "rb")
file1<- readBin(conne, double(), size=4, n=360*720, signed=TRUE)
file1[file1 != -9999] <- file1[file1 != -9999]*10
close(conne)
fileName <- sprintf("C:\\New folder (11)\\NewFile%d.bin", b)
writeBin(as.integer(file1), fileName, size = 2)
}
Result:
Error in sprintf("C:\\New folder (11)\\NewFile%d.bin", :
invalid format '%d'; use format %s for character objects
I used %s as suggested by R.But the files from 1:365 were totally empty
The %d is a placeholder for a integer variable inside a string. Therefore, when you use sprintf(%d, var), var must be an integer.
In your case, the variable b is a string (or a character object). So, you use the placeholder for string variables, which is %s.
Now, if your files are empty, there must be something wrong elsewhere in your code. You should ask another question more specific to it.