Reputation: 145
I am new to R and I am having some difficulty understanding how to return the output of a S3 class for a function. I have some text and I need to write a summary method for it that will count the number of words in the text and the frequency of the top 3 words in the text. I have a function countwords that will count the words. The text is above the code:
text = 'The time of year was spring the sun shone for the birds who were not singing yet. The Local farmer was out in the fields preparing for the summer ahead. He had a spring in his step, for he was whistling.'
#counts the number of words in the text
countwords = function(x) {
# Read in the words from the text and separate into a vector
txt = unlist(strsplit(x,' '))
# Loop through each word
k = 0
for(i in 1:length(txt)) {
k = k + 1
}
return(k)
}
countwords(firstpar)
How do I return the output of this as an s3 class? How do I write a summary method/function? to count the words and also the top 3 words in the text? I am new to R and need some help explaining S3 classes and methods and functions. Is a function the same as a method?
Thank you
Upvotes: 2
Views: 888
Reputation: 174778
Here's one way to do both things, which illustrates the way to add a class and not have to write all the methods you might need for that class. I've also tweaked your function a bit to be more efficient and to work on a vector of strings as inputs. You also don't need the return()
call; IIRC it is slightly more efficient to not call return
explicitly but to use the fact that R returns automatically the result of the final statement in the function.
mystring <- "The time of year was spring the sun shone for the birds who were not singing yet. The Local farmer was out in the fields preparing for the summer ahead. He had a spring in his step, for he was whistling."
# counts the number of words in the text
countwords <- function(x) {
# Read in the words from the text and separate into a vector
txt <- strsplit(x, " ")
n <- sapply(txt, length)
top3 <- lapply(txt, function(x) names(tail(sort(table(x)), 3)))
out <- list(n = n, top3 = top3)
class(out) <- c("mysummary", "list")
out # implied that we return out here
}
countwords(mystring)
This gets us:
> countwords(mystring)
$n
[1] 41
$top3
$top3[[1]]
[1] "for" "was" "the"
attr(,"class")
[1] "mysummary" "list"
Which isn't pretty, but we can sort that later with a print
method. Notice that this is just a list, hence I used class(out) <- c("mysummary", "list")
as my S3 class(es) to indicate inheritance from class "list"
> str(countwords(mystring))
List of 2
$ n : int 41
$ top3:List of 1
..$ : chr [1:3] "for" "was" "the"
- attr(*, "class")= chr [1:2] "mysummary" "list"
That means we can subset it like any list without writing those methods:
> cw <- countwords(mystring)
> cw$n
[1] 41
> cw[[2]]
[[1]]
[1] "for" "was" "the"
That's all you really need for an S3 class. This doesn't change even if you stick this in a package. (What you need to do extra then relates to methods for ytou class and we don't have any of those as we inherit from class "list"
> inherits(cw, "list")
[1] TRUE
If you want to add a print
method we can just do:
`print.mysummary` <- function(x, ...) {
writeLines(strwrap("Number of words:", prefix = "\n"))
print(x$n, ...)
writeLines(strwrap("Top 3 Words:", prefix = "\n"))
print(x$top3, ...)
invisible(x)
}
which then produces:
> cw
Number of words:
[1] 41
Top 3 Words:
[[1]]
[1] "for" "was" "the"
Upvotes: 4
Reputation: 2992
To make @Roland's comments a little more explicit:
First, you'll create an S3 class of your own, let's call it myTextClass
, and then assign the class
attribute of your object text
.
class(text)<-c('myTextClass')
At this point, the class myTextClass
isn't doing much for us, so we need to make a method for this class. In particular, we'll make a new method for the summary
function so that whenever summary
encounters and object of class myTextClass
it will execute our desired method instead of any others.
summary.myTextClass<-function(t){
z1<- paste('Length of text is ', length(unlist(strsplit(t,' '))) ,sep=' ')
tb<-table(unlist(strsplit(t,' ')))
topWords<-paste(names(tb)[1:3],tb[1:3],sep=':')
z2<- paste(c('Top words are... ', topWords) ,collapse=' ')
return(c(z1,z2))
}
This method does some of the basic things you mention such as word counts and so on. Now when we call the generic function summary
on an object of class myTextClass
, this particular method will be called.
summary(text)
[1] "Length of text is 41" "Top words are... a:1 ahead.:1 birds:1"
Upvotes: 0