Logo ROOT  
Reference Guide
RDataFramePyz.cxx
Go to the documentation of this file.
1 // Author: Stefan Wunsch CERN 04/2019
2 // Original PyROOT code by Wim Lavrijsen, LBL
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "CPyCppyy.h"
13 #include "CPPInstance.h"
14 #include "ProxyWrappers.h"
15 #include "PyROOTPythonize.h"
16 #include "RConfig.h"
17 #include "TInterpreter.h"
18 #include "CPyCppyy/API.h"
19 
20 #include <utility> // std::pair
21 #include <sstream> // std::stringstream
22 
23 ////////////////////////////////////////////////////////////////////////////
24 /// \brief Make an RDataFrame from a dictionary of numpy arrays
25 /// \param[in] pydata Dictionary with numpy arrays
26 ///
27 /// This function takes a dictionary of numpy arrays and creates an RDataFrame
28 /// using the keys as column names and the numpy arrays as data.
30 {
31  if (!pydata) {
32  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Invalid Python object.");
33  return NULL;
34  }
35 
36  if (!PyDict_Check(pydata)) {
37  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Python object is not a dictionary.");
38  return NULL;
39  }
40 
41  if (PyDict_Size(pydata) == 0) {
42  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Dictionary is empty.");
43  return NULL;
44  }
45 
46 
47  // Add PyObject (dictionary) holding RVecs to data source
48  std::stringstream code;
49  code << "ROOT::Internal::RDF::MakeNumpyDataFrame(";
50  std::stringstream pyaddress;
51  auto pyvecs = PyDict_New();
52 #ifdef _MSC_VER
53  pyaddress << "0x";
54 #endif
55  pyaddress << pyvecs;
56  code << "reinterpret_cast<PyObject*>(" << pyaddress.str() << "), ";
57 
58  // Iterate over dictionary, convert numpy arrays to RVecs and put together interpreter code
59  PyObject *key, *value;
60  Py_ssize_t pos = 0;
61  const auto size = PyObject_Size(pydata);
62  auto counter = 0u;
63  while (PyDict_Next(pydata, &pos, &key, &value)) {
64  // Get name of key
65  if (!CPyCppyy_PyText_Check(key)) {
66  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Dictionary key is not convertible to a string.");
67  return NULL;
68  }
69  std::string keystr = CPyCppyy_PyText_AsString(key);
70 
71  // Convert value to RVec and attach to dictionary
72  auto pyvec = PyROOT::AsRVec(NULL, value);
73  if (pyvec == NULL) {
74  PyErr_SetString(PyExc_RuntimeError,
75  ("Object not convertible: Dictionary entry " + keystr + " is not convertible with AsRVec.").c_str());
76  return NULL;
77  }
78  PyDict_SetItem(pyvecs, key, pyvec);
79  Py_DECREF(pyvec);
80 
81  // Add pairs of column name and associated RVec to signature
82  std::string vectype = Cppyy::GetScopedFinalName(((CPyCppyy::CPPInstance*)pyvec)->ObjectIsA());
83  std::stringstream vecaddress;
84 #ifdef _MSC_VER
85  vecaddress << "0x";
86 #endif
87  vecaddress << ((CPyCppyy::CPPInstance*)pyvec)->GetObject();
88  code << "std::pair<std::string, " << vectype << "*>(\"" + keystr
89  << "\", reinterpret_cast<" << vectype+ "*>(" << vecaddress.str() << "))";
90  if (counter != size - 1) {
91  code << ",";
92  } else {
93  code << ");";
94  }
95  counter++;
96  }
97 
98  // Create RDataFrame and build Python proxy
99  const auto err = gInterpreter->Declare("#include \"ROOT/RNumpyDS.hxx\"");
100  if (!err) {
101  PyErr_SetString(PyExc_RuntimeError, "Failed to find \"ROOT/RNumpyDS.hxx\".");
102  return NULL;
103  }
104  const auto codeStr = code.str();
105  auto address = (void*) gInterpreter->Calc(codeStr.c_str());
106  const auto pythonOwns = true;
107  auto pyobj = CPyCppyy::Instance_FromVoidPtr(address, "ROOT::RDataFrame", pythonOwns);
108 
109  // Bind pyobject holding adopted memory to the RVec
110  if (PyObject_SetAttrString(pyobj, "__data__", pyvecs)) {
111  PyErr_SetString(PyExc_RuntimeError, "Object not convertible: Failed to set dictionary as attribute __data__.");
112  return NULL;
113  }
114  Py_DECREF(pyvecs);
115 
116  return pyobj;
117 }
CPPInstance.h
PyObject
_object PyObject
Definition: PyMethodBase.h:42
PyROOTPythonize.h
gInterpreter
#define gInterpreter
Definition: TInterpreter.h:560
CPyCppyy::CPPInstance
Definition: CPPInstance.h:26
CPyCppyy_PyText_Check
#define CPyCppyy_PyText_Check
Definition: CPyCppyy.h:95
PyROOT::MakeNumpyDataFrame
PyObject * MakeNumpyDataFrame(PyObject *self, PyObject *obj)
Make an RDataFrame from a dictionary of numpy arrays.
Definition: RDataFramePyz.cxx:29
ProxyWrappers.h
PyROOT::AsRVec
PyObject * AsRVec(PyObject *self, PyObject *obj)
Adopt memory of a Python object with array interface using an RVec.
Definition: RVecPyz.cxx:28
CPyCppyy.h
Cppyy::GetScopedFinalName
RPY_EXPORTED std::string GetScopedFinalName(TCppType_t type)
Definition: clingwrapper.cxx:1161
CPyCppyy_PyText_AsString
#define CPyCppyy_PyText_AsString
Definition: CPyCppyy.h:97
API.h
TInterpreter.h
CPyCppyy::Instance_FromVoidPtr
CPYCPPYY_EXTERN PyObject * Instance_FromVoidPtr(void *addr, const std::string &classname, bool python_owns=false)
Definition: API.cxx:107
int