Skip to content

[Rcpp-devel] Help with accessing and manipulating List objects

9 messages · Dirk Eddelbuettel, Romain Francois, Kevin Ushey +1 more

#
Dear Romain, and other Rcpp-devel members.

I need some help. I've looked into the code Romain posted a few weeks ago,
and I see that I can't change the example from working on double to working
on String.
I keep running into the error of:
no matching function for call to
'range_wrap_dispatch__impl(__gnu_cxx::__normal_iterator<const:Rcpp::String*,str::vector<Rcpp::String>
Any ideas what I might be doing wrong?
(thanks up front)

Here is the code I used in sourceCpp:


////////////////////////////////////
bool is_list(RObject x){
    return TYPEOF(x) == VECSXP ;
}

bool is_string(RObject x){
    return TYPEOF(x) == STRSXP && Rf_length(x) == 1 ;
}

bool is_logical(RObject x){
    return TYPEOF(x) == LGLSXP && Rf_length(x) == 1 ;
}


bool is_leaf(RObject x){
    if( TYPEOF(x) != REALSXP ) return false ;
    if( !is_logical( x.attr("leaf") ) ) return false ;
    bool leaf = x.attr( "leaf" ) ;
    return leaf; // either TRUE or FALSE. But often, if it exists - it is
TRUE.
}

String get_label(RObject x){
    String label = x.attr( "label" ) ;
    return label; // either TRUE or FALSE. But often, if it exists - it is
TRUE.
}



//void process( List data, std::vector<double>& results ){
//void process( List data, CharacterVector& results ){
//void process( List data, std::vector<std::string>& results){
void process( List data, std::vector<String>& results ){
    for( int i=0; i<data.size(); i++){
        if( is_list( data[i] ) ){
            // recurse
            process( data[i], results ) ;
        } else if( is_leaf( data[i] ) ){
            // we want to collect them. we can use the NumericVector class
            // wince we know this is a numeric vector.
            // (it is tested in is_leaf)
//            NumericVector y = data[i] ;
//            String x_label = y.attr("label");
            String x_label = get_label(data[i]);


            results.push_back(x_label);
            // loop through the values and add them to the results vector
//            for( int j=0; j<x.size(); j++){
//                results.push_back( x[j] ) ;
//            }
        } // else do nothing
    }
}

// [[Rcpp::export]]
CharacterVector extract_fun(List x){
//    std::vector<double> results ;
    std::vector<String> results ;
    process(x, results) ;
    return wrap(results) ;
}

/*** R


x <- list(a = 1, b = 2, c = list(ca = 3, cb = 4, 5), 6)
attr(x[[1]], "leaf") = TRUE
attr(x[[1]], "label") = "leaf 1"
attr(x[[3]][[1]], "leaf") = TRUE

attr(x[[2]], "leaf") = TRUE
attr(x[[2]], "label") = "leaf 2"

str(x)

extract_fun(x)

*/






















----------------Contact
Details:-------------------------------------------------------
Contact me: Tal.Galili at gmail.com |
Read me: www.talgalili.com (Hebrew) | www.biostatistics.co.il (Hebrew) |
www.r-statistics.com (English)
----------------------------------------------------------------------------------------------



On Tue, Jul 23, 2013 at 3:23 PM, Romain Francois
<romain at r-enthusiasts.com>wrote:

            
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.r-forge.r-project.org/pipermail/rcpp-devel/attachments/20130816/da687aa3/attachment-0001.html>
#
Tal,

You were close.  The error you got indicated that some of (our) wrapping
around (our) class String was missing somehow.  String is pretty new; Romain
just added it a few month ago under funding by Hadley -- and I am still
pretty unfamiliar with it.

Which is why I always go back to std::string. So I converted your code back,
which then built find but ran into one run-time error: you didn't test for
the attribute before extracting it.  That is corrected too.  So your
list-walker is below, with some extra verbose stdout prints.

Hope this helps, it is a nice example and always was a very good question.

Dirk


// Code first


#include <Rcpp.h>
using namespace Rcpp;


bool is_list(RObject x){
    return TYPEOF(x) == VECSXP ;
}

bool is_string(RObject x){
    return TYPEOF(x) == STRSXP && Rf_length(x) == 1 ;
}

bool is_logical(RObject x){
    return TYPEOF(x) == LGLSXP && Rf_length(x) == 1 ;
}


bool is_leaf(RObject x){
    if( TYPEOF(x) != REALSXP ) return false ;
    if( !is_logical( x.attr("leaf") ) ) return false ;
    bool leaf = x.attr( "leaf" ) ;
    return leaf; // either TRUE or FALSE. But often, if it exists - it is TRUE.
}

std::string get_label(RObject x){
    std::string label = "<empty>";
    if (x.hasAttribute("label")) {
	label = as<std::string>(x.attr( "label" )) ;
    }
    return label; // either TRUE or FALSE. But often, if it exists - it is TRUE.
}


void process( List data, std::vector<std::string>& results){
    Rcout << "List with " << data.size() << " elements\n";
    for( int i=0; i<data.size(); i++){
        if( is_list( data[i] ) ){
            // recurse
            Rcout << "Recursing into list\n";
            process( data[i], results ) ;
        } else if( is_leaf( data[i] ) ){
            Rcout << "Looking at leaf\n";
            // we want to collect them. we can use the NumericVector class
            // wince we know this is a numeric vector.
            std::string x_label = get_label(data[i]);
            results.push_back(x_label);
        } // else do nothing
    }
}

// [[Rcpp::export]]
std::vector<std::string> extract_fun(List x){
    std::vector<std::string> results ;
    process(x, results) ;
    return(results) ;
}

/*** R

x <- list(a = 1, b = 2, c = list(ca = 3, cb = 4, 5), 6)
attr(x[[1]], "leaf") = TRUE
attr(x[[1]], "label") = "leaf 1"
attr(x[[3]][[1]], "leaf") = TRUE

attr(x[[2]], "leaf") = TRUE
attr(x[[2]], "label") = "leaf 2"

str(x)

extract_fun(x)

*/


// Output below:
List with 4 elements
Looking at leaf
Looking at leaf
Recursing into list
List with 3 elements
Looking at leaf
[1] "leaf 1"  "leaf 2"  "<empty>"
#
Hello Dirk,
Your modifications/corrections are VERY helpful, thank you! (also, thank
you for the general kind words and ongoing support, they are much
appreciated)

One more question/improvement - is it possible to have it return whatever
value is inside the "label" attr?

For example, running the following would result in an error:

/*** R
x <- list(a = 1, b=2, 6)
attr(x[[2]], "leaf") = TRUE
attr(x[[2]], "label") = 2
extract_fun(x)

## output:
## Error: expecting a string
/*

Is there a way to either coerce/force "2" to become a string / or to return
it as what it is? (a numeric/double value)?
I thought that was what
        label = as<std::string>(x.attr( "label" )) ;
was doing - but apparently it does not coerce non-string into strings


Any suggestions?


Tal










----------------Contact
Details:-------------------------------------------------------
Contact me: Tal.Galili at gmail.com |
Read me: www.talgalili.com (Hebrew) | www.biostatistics.co.il (Hebrew) |
www.r-statistics.com (English)
----------------------------------------------------------------------------------------------
On Fri, Aug 16, 2013 at 2:47 PM, Dirk Eddelbuettel <edd at debian.org> wrote:

            
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.r-forge.r-project.org/pipermail/rcpp-devel/attachments/20130816/74dacbb8/attachment.html>
#
Hi Tal,
On 16 August 2013 at 15:48, Tal Galili wrote:
| Hello Dirk,
| Your modifications/corrections are VERY helpful, thank you! (also, thank you
| for the general kind words and ongoing support, they are much appreciated)
| 
| One more question/improvement - is it possible to have it return whatever value
| is inside the "label" attr?

Yes, sure. 
 
| For example, running the following would result in an error:
| 
| /*** R
| x <- list(a = 1, b=2, 6)
| attr(x[[2]], "leaf") = TRUE
| attr(x[[2]], "label") = 2
| extract_fun(x)
| 
| ## output:
| ## Error: expecting a string
| /*
| 
| Is there a way to either coerce/force "2" to become a string / or to return it
| as what it is? (a numeric/double value)?
| I thought that was what
| ??????? label = as<std::string>(x.attr( "label" )) ;
| was doing - but apparently it does not coerce non-string into strings
| 
| 
| Any suggestions?

Am at work and a little pressed for time, so just random thoughts:

  -- R has dynamic typing. An attribute could be 'anything'

  -- C++ is typed. You either must agree with yourself on a type, or do the
     casting. Converting int to string, or vice verse, is pretty common, and
     Google will show you examples.

  -- You could try to combine the approaches and just pick leaf elements and
     stick them into a a List object as SEXP (using wrap(...)).

This is a nice example of a recursize list / tree walker. We'll get it
polished and onto the Rcpp Gallery in no time.

Dirk

| 
| Tal
| 
| 
| 
| 
| 
| 
| 
| 
| 
| 
| ----------------Contact
| Details:-------------------------------------------------------
| Contact me: Tal.Galili at gmail.com |?
| Read me: www.talgalili.com (Hebrew) | www.biostatistics.co.il (Hebrew) |
| www.r-statistics.com (English)
| ----------------------------------------------------------------------------------------------
| 
| 
|
| On Fri, Aug 16, 2013 at 2:47 PM, Dirk Eddelbuettel <edd at debian.org> wrote:
| 
| 
|     Tal,
| 
|     You were close. ?The error you got indicated that some of (our) wrapping
|     around (our) class String was missing somehow. ?String is pretty new;
|     Romain
|     just added it a few month ago under funding by Hadley -- and I am still
|     pretty unfamiliar with it.
| 
|     Which is why I always go back to std::string. So I converted your code
|     back,
|     which then built find but ran into one run-time error: you didn't test for
|     the attribute before extracting it. ?That is corrected too. ?So your
|     list-walker is below, with some extra verbose stdout prints.
| 
|     Hope this helps, it is a nice example and always was a very good question.
| 
|     Dirk
| 
| 
|     // Code first
| 
| 
|     #include <Rcpp.h>
|     using namespace Rcpp;
| 
| 
|     bool is_list(RObject x){
|     ? ? return TYPEOF(x) == VECSXP ;
|     }
| 
|     bool is_string(RObject x){
|     ? ? return TYPEOF(x) == STRSXP && Rf_length(x) == 1 ;
|     }
| 
|     bool is_logical(RObject x){
|     ? ? return TYPEOF(x) == LGLSXP && Rf_length(x) == 1 ;
|     }
| 
| 
|     bool is_leaf(RObject x){
|     ? ? if( TYPEOF(x) != REALSXP ) return false ;
|     ? ? if( !is_logical( x.attr("leaf") ) ) return false ;
|     ? ? bool leaf = x.attr( "leaf" ) ;
|     ? ? return leaf; // either TRUE or FALSE. But often, if it exists - it is
|     TRUE.
|     }
| 
|     std::string get_label(RObject x){
|     ? ? std::string label = "<empty>";
|     ? ? if (x.hasAttribute("label")) {
|     ? ? ? ? label = as<std::string>(x.attr( "label" )) ;
|     ? ? }
|     ? ? return label; // either TRUE or FALSE. But often, if it exists - it is
|     TRUE.
|     }
| 
| 
|     void process( List data, std::vector<std::string>& results){
|     ? ? Rcout << "List with " << data.size() << " elements\n";
|     ? ? for( int i=0; i<data.size(); i++){
|     ? ? ? ? if( is_list( data[i] ) ){
|     ? ? ? ? ? ? // recurse
|     ? ? ? ? ? ? Rcout << "Recursing into list\n";
|     ? ? ? ? ? ? process( data[i], results ) ;
|     ? ? ? ? } else if( is_leaf( data[i] ) ){
|     ? ? ? ? ? ? Rcout << "Looking at leaf\n";
|     ? ? ? ? ? ? // we want to collect them. we can use the NumericVector class
|     ? ? ? ? ? ? // wince we know this is a numeric vector.
|     ? ? ? ? ? ? std::string x_label = get_label(data[i]);
|     ? ? ? ? ? ? results.push_back(x_label);
|     ? ? ? ? } // else do nothing
|     ? ? }
|     }
| 
|     // [[Rcpp::export]]
|     std::vector<std::string> extract_fun(List x){
|     ? ? std::vector<std::string> results ;
|     ? ? process(x, results) ;
|     ? ? return(results) ;
|     }
| 
|     /*** R
| 
|     x <- list(a = 1, b = 2, c = list(ca = 3, cb = 4, 5), 6)
|     attr(x[[1]], "leaf") = TRUE
|     attr(x[[1]], "label") = "leaf 1"
|     attr(x[[3]][[1]], "leaf") = TRUE
| 
|     attr(x[[2]], "leaf") = TRUE
|     attr(x[[2]], "label") = "leaf 2"
| 
|     str(x)
| 
|     extract_fun(x)
| 
|     */
| 
| 
|     // Output below:
| 
|     > extract_fun(x)
|     List with 4 elements
|     Looking at leaf
|     Looking at leaf
|     Recursing into list
|     List with 3 elements
|     Looking at leaf
|     [1] "leaf 1" ?"leaf 2" ?"<empty>"
| 
| 
| 
|     --
|     Dirk Eddelbuettel | edd at debian.org | http://dirk.eddelbuettel.com
| 
|
#
Le 16/08/13 16:19, Dirk Eddelbuettel a ?crit :
I'd wait for the next Rcpp to be out for putting this example on the 
gallery. because then we would be able to use the more elegant is<> 
rather than these is_list, ...

Romain
#
Le 16/08/13 14:48, Tal Galili a ?crit :
String should know how to do that. I'd recommend you to use String, 
which is an Rcpp class rather than std::string.

For example a String knows what to do with an int:

#include <Rcpp.h>
using namespace Rcpp ;

// [[Rcpp::export]]
String strings(){
     String s( 5 ) ;
     return s ;
}

 > strings()
[1] "5"

  
    
#
Just a suggestion re: recursion with Rcpp, yet another nice way we can
recurse is through using the sugar lapply:

-----

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
RObject add_attr(RObject x_) {
  RObject x = clone(x_);
  if (is<List>(x)) {
    x = wrap( lapply( as<List>(x), add_attr ) );
  } else {
    x.attr("type") = "Other";
  }
  return x;
}

/*** R
add_attr( list(x=list(a=1, b=2, c=3), y=list( list( d=4 ) ) ) )
*/

-----

If we want to operate on specific elements as well, we can use is<T> and
as<T> together, and then operate on objects as needed.

It lets us avoid an explicit for loop, and 'feels' more like R.

-Kevin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.r-forge.r-project.org/pipermail/rcpp-devel/attachments/20130816/56b9f117/attachment.html>
#
Dear Dirk, Romain, Kevin, and others.

Since I am currently actively working on extending the dendrogram object in
R, I have a real interest in finding and solving some of the major
bottlenecks of dendrogram manipulation.
Thank you all for all of your help and suggestions thus far.

I took the liberty of starting a github repo for a new R package
"RcppDend", you can see it here:
https://github.com/talgalili/RcppDend

It currently contains the function for label extraction from dendrogram (a
mixture of the code Romain and Dirk wrote, with some minor modifications by
me).
The Rcpp code is here:
https://github.com/talgalili/RcppDend/blob/master/src/labels_dendrogram.cpp
And the R code (wrapping it a bit, and adding documentation), is here:
https://github.com/talgalili/RcppDend/blob/master/R/RcppDend.r

Currently, I can get the labels.dendrogram function to work when I'm
compiling it in a local session, but *the function fails to work when
loaded from this package*. If you have suggestions on how to fix it, I'd be
happy to read.

And in general, I welcome any code contributions/commits.

Best,
Tal






----------------Contact
Details:-------------------------------------------------------
Contact me: Tal.Galili at gmail.com |
Read me: www.talgalili.com (Hebrew) | www.biostatistics.co.il (Hebrew) |
www.r-statistics.com (English)
----------------------------------------------------------------------------------------------
On Fri, Aug 16, 2013 at 9:04 PM, Kevin Ushey <kevinushey at gmail.com> wrote:

            
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.r-forge.r-project.org/pipermail/rcpp-devel/attachments/20130816/bc4097f0/attachment.html>
#
Just to update everyone that I've found the bug in the package, and now the
RcppDend package works fine. You can see the relevant commit here:
https://github.com/talgalili/RcppDend/commit/25d456571f2f1e52f1f9aa8ac6bc3a7fcb832e65

The problem was that my NAMESPACE file was missing the two lines:
useDynLib(RcppDend)
exportPattern("^[[:alpha:]]+")

The reason it was missing them was because I am using Rstudio+roxygen2, and
had NAMESPACE editing turned on, and it appears to erase these lines. This
had caused R to not find the Rcpp functions, and resulted in the error:

Error in .Call("RcppDend_Rcpp_labels_dendrogram", PACKAGE = "RcppDend") :
  "RcppDend_Rcpp_labels_dendrogram

" not available for .Call() for package "RcppDend"


I have submitted a bug report for RStudio/roxygen2:
http://support.rstudio.org/help/discussions/problems/8418-rcpp-packages-fails-when-using-roxygen2-to-handle-namespace
https://github.com/klutometis/roxygen/issues/130



Future tweaks/commits/suggestions to RcppDend
<https://github.com/talgalili/RcppDend>are most welcomed.

Best,
Tal






----------------Contact
Details:-------------------------------------------------------
Contact me: Tal.Galili at gmail.com |
Read me: www.talgalili.com (Hebrew) | www.biostatistics.co.il (Hebrew) |
www.r-statistics.com (English)
----------------------------------------------------------------------------------------------
On Fri, Aug 16, 2013 at 9:48 PM, Tal Galili <tal.galili at gmail.com> wrote:

            
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.r-forge.r-project.org/pipermail/rcpp-devel/attachments/20130817/3a47b6b0/attachment-0001.html>