Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
calculate_r_expression [2018/09/03 14:24] francisco [R Script Outputs] |
calculate_r_expression [2026/07/22 18:13] (current) hermann |
||
|---|---|---|---|
| Line 3: | Line 3: | ||
| ===== Description ===== | ===== Description ===== | ||
| - | The functor 'Calculate R Expression' allows Dinamica EGO to call R externally and process the script outputs as if they were part of Dinamica itself. For an overview of how Dinamica and R can be linked together check the documentation about [[reference_book:r_coupling|R Coupling]]. | + | This is a **[[ego_script#container_functors|container functor]]** that calls R externally with the user-defined expression. Like the other calculator functors, data is connected through hook functors placed inside its ''<nowiki>{{ … }}</nowiki>'' block. |
| ===== Inputs ===== | ===== Inputs ===== | ||
| - | ^ Name ^ Type ^ Description ^ | + | ^ Name ^ Type ^ Description ^ |
| - | | Expression | [[String Type]] | The expression that will run on R. | | + | | Expression | [[ego_script#constants|Code]] | The expression that will run on R. ''Code'' values cannot be written as plain text constants in EGO Script — see [[#writing_the_expression_in_ego_script|Writing the expression in EGO Script]] below. | |
| - | | Treat Warning As Errors | [[Boolean Value Type]] | Warnings on R script will be considered errors. | | + | | Treat Warning As Errors | [[Boolean Value Type]] | Warnings raised by the R script will be treated as errors. | |
| ===== Optional Inputs ===== | ===== Optional Inputs ===== | ||
| Line 15: | Line 15: | ||
| None. | None. | ||
| - | ===== Output ===== | + | ===== Outputs ===== |
| - | ^ Name ^ Type ^ Description ^ | + | ^ Name ^ Type ^ Description ^ |
| - | | result | [[Struct Type]] | A struct containing the output values generated by the expression. | | + | | result | [[struct_type|Struct]] | A struct containing the output values generated by the expression. | |
| - | + | ||
| - | ===== Group ===== | + | |
| - | + | ||
| - | [[Functor List#Integration | Integration]] | + | |
| ===== Notes ===== | ===== Notes ===== | ||
| - | There are two ways to setup the 'Calculate R Expression' functor: | + | ==== Expression inputs ==== |
| - | ==== Dinamica EGO Enhancement Plugin ==== | + | Data is passed into the expression through **hook** functors placed inside the container's ''<nowiki>{{ … }}</nowiki>'' block — the same [[ego_script#verbose_form|verbose form]] hook mechanism used by the calculator functors. Hooks can be added using the //Create a hook// button on the functor bar, or by dragging them in individually. |
| - | Simply download and install the [[plugins_4|Dinamica EGO Enhancement Plugin]]. It contains everything you need to run R scripts inside Dinamica EGO. | + | * Tables and lookup tables → [[Number Table]] → available in R as ''t1'', ''t2'', …, ''t100'' |
| + | * Scalar values → [[Number Value]] → available as ''v1'', ''v2'', …, ''v100'' | ||
| + | * Strings → [[Number String]] → available as ''s1'', ''s2'', …, ''s100'' | ||
| - | ==== Local R Installation ==== | + | Maps cannot be connected — this functor has no cell context. There is no shorthand notation; see [[calculate_functors|Calculate Functors — Complete Operator Documentation]] for the general hook mechanism and syntax. |
| - | Additionally, users can specify and use their own local R installation for execution. The pre-requisites are: | + | === Tables and lookup tables === |
| - | * R needs to be at least at the 3.3.1 version. | + | Lookup tables and tables require extra care, since each is transferred to R using a different representation. |
| - | * ''Rscript.exe'' must be valid and located at the ''<installation folder>/bin'' sub-folder. | + | A **lookup table** is transferred as a list with two columns, ''Key'' and ''Value''. Each column is accessed with the ''$'' operator: |
| - | * [[external communication|Dinamica package]] for R must be installed and at the latest version. | + | <code rsplus> |
| + | # Access the third key and its corresponding value in lookup table t1 | ||
| + | patchId <- t1$Key[3]; | ||
| + | patchArea <- t1$Value[3]; | ||
| + | </code> | ||
| - | To specify your custom R installation, go to Tools --> Options --> Integration tab and select //Use alternative R installation for Calculate R Expression//: | + | A **table** is transferred as a [[https://en.wikibooks.org/wiki/R_Programming/Working_with_data_frames|DataFrame]], with each column likewise accessed using ''$''. Two conventions apply to tables in either direction: |
| - | {{:dinamica_options_use_alternative_r_install.png?nolink|}} | + | * **Key columns** are marked by an asterisk (''*'') appended to their column name — this is the same convention used throughout Dinamica EGO's table representation (see [[calculate_functors#connecting_data_inputs|Connecting Data Inputs]]). A key column can be Real or String, the same as any other column. |
| + | * Each column's type is inferred from its data, the same rule that applies to any table (see [[ego_script#constants|Constants]]): a numeric vector produces a Real column, a character vector produces a String column. R automatically converts string columns to **Factors** inside a ''data.frame'', and a Factor is neither of those — Dinamica requires plain **Character Vectors** to infer String correctly. Always build tables with ''stringsAsFactors = FALSE'' to prevent this conversion. | ||
| + | For further detail on the underlying table representation, see [[external_communication#table|External Communication]]. | ||
| - | ---- | + | ==== Expression outputs ==== |
| - | ===== Usage ===== | + | Values are returned to Dinamica by calling one of the following functions from the R script. Every call requires an identifier as its first parameter — the name Dinamica uses to place the value into the output struct — and the value itself, which can be constructed inline, as in the examples below, or supplied as a variable: |
| - | The functor is available under the //Integration// tab at the library. | + | ^ Function ^ Output type ^ Notes ^ Example ^ |
| + | | ''outputDouble()'' | Real | Accepts any numeric value. | ''outputDouble("myDouble", 3.14)'' | | ||
| + | | ''outputNumberVector()'' | Tuple | Accepts any collection of numbers. | ''outputNumberVector("myTuple", c(1:10))'' | | ||
| + | | ''outputString()'' | String | Accepts any string value. | ''outputString("myString", "This is a string")'' | | ||
| + | | ''outputLookupTable()'' | [[Lookup Table Type|Lookup Table]] | Requires two number vectors of equal length — one for the keys, one for the values. Lookup tables are always Real-typed on both sides; there is no String option. | ''outputLookupTable("myLUT", c(1:10), c(1:10) * 10)'' | | ||
| + | | ''outputTable()'' | Table | Requires a table built with the [[https://www.r-tutor.com/r-introduction/data-frame|data.frame]] function, using ''stringsAsFactors = FALSE'' as described above. Its optional second parameter (default ''1'') sets how many leading columns, from the left, are key columns. | ''outputTable("myTable", data.frame(State = c("Massachusetts", "Massachusetts"), City = c("Boston", "Chelsea"), Population = c(667137, 39398), stringsAsFactors = FALSE), 2)'' | | ||
| - | ===== R Script Inputs ===== | + | ==== Retrieving outputs ==== |
| - | To pass input parameters from Dinamica EGO to R, hooks must be placed to collect data. You can use the //Create a hook// button on the selected functor bar or drag the hooks individually. The valid hook types are: | + | ''CalculateRExpression'' returns a single [[struct_type|Struct]] value (via its ''result'' output port) containing every value passed to an ''output*()'' function. To retrieve individual values from that struct, use the corresponding functor from the //Integration// group: |
| - | * [[Number Table]] | + | ^ Functor ^ Retrieves ^ |
| + | | [[Extract Struct Number]] | A value passed to ''outputDouble()'' | | ||
| + | | [[Extract Struct Tuple]] | A value passed to ''outputNumberVector()'' | | ||
| + | | [[Extract Struct String]] | A value passed to ''outputString()'' | | ||
| + | | [[Extract Struct Lookup Table]] | A value passed to ''outputLookupTable()'' | | ||
| + | | [[Extract Struct Table]] | A value passed to ''outputTable()'' | | ||
| - | * [[Number Value]] | + | Each functor takes two inputs: the ''Struct'' returned by ''CalculateRExpression'', and the name of the entry to extract as a string constant. |
| - | * [[Number String]] | + | ==== Installing packages ==== |
| - | To access the passed inputs on your R script, follow Dinamica EGO naming conventions and use ''t[0-99]'', ''v[0-99]'', ''s[0-99]'' for each type respectively. For example: | + | Packages are installed by calling ''dinamicaPackage("packageName")'' from within the expression — one call per package. Unlike [[calculate_python_expression|Calculate Python Expression]], there is no separate input port for listing packages; ''dinamicaPackage()'' is the only mechanism available. |
| - | <code rsplus> | + | ''dinamicaPackage("packageName")'' also acts as R's ''library()'' call: when the package name matches the name of the module to load, calling it both installs the package (if not already present) and loads it, in a single call. |
| - | # Will store the 5th key from the passed Lookup Table in the variable 'testing' | + | |
| - | testing <- t1$Keys[ 5 ]; | + | |
| - | # Store the second passed String. | + | <code rsplus> |
| - | secondString <- s2; | + | dinamicaPackage("moments"); |
| - | + | ||
| - | # Store the 10th passed Value. | + | |
| - | tempNumber <- v10; | + | |
| </code> | </code> | ||
| - | ===== R Script Outputs ===== | + | ==== Setup ==== |
| - | To output data back to Dinamica EGO, the user must call a set of functions designed for this functionality: | + | There are two ways to run R scripts from ''CalculateRExpression'' — though only the local installation is available on Linux. |
| - | ^ Function name ^ Output Data Type ^ Usage Example ^ | + | === Dinamica EGO Enhancement Plugin === |
| - | | outputDouble() | Real | outputDouble( "myDouble", 3.14 ) | | + | |
| - | | outputNumberVector() | Tuple | outputNumberVector( "myTuple", c(1:10) ) | | + | |
| - | | outputString() | String | outputString( "myString", "This is a string" ) | | + | |
| - | | outputLookupTable() | LookupTable | outputLookupTable( "myLUT", c(1:10), c(1:10) * 10 ) | | + | |
| - | | outputTable() | Table | outputTable( "myTable", data.frame(State = c("Massachusetts", "Massachusetts"), City = c("Boston", "Chelsea"), Population = c(667137, 39398), stringsAsFactors = FALSE), 2 ) | | + | |
| - | In the examples above, data was constructed inside the calling functions. You can also specify variables as parameters. | + | Windows only. Download and install the [[plugins_4|Dinamica EGO Enhancement Plugin]]. It contains everything needed to run R scripts inside Dinamica EGO, with no further configuration. |
| - | <note important>All output functions require an identifier as the first parameter, that's the name Dinamica uses for retrieving the correct data (to put into the struct output).</note> | + | === Local R installation === |
| - | Keep in mind: | + | On Linux, this is the only option — Dinamica EGO always uses the R installation already present on the system. On Windows, it can be used as an alternative to the plugin. Either way, it requires: |
| - | * You can pass any kind of numeric value on the ''outputDouble'' function. | + | * R installed on the machine, with the ''Rscript'' executable (''Rscript.exe'' on Windows) present in its ''bin'' sub-folder. |
| + | * The [[external_communication|Dinamica package]] for R installed and at its latest version. | ||
| - | * Collections of numbers are valid number vector's. | + | On Windows, this alternative installation is selected in the Dinamica EGO GUI by going to //Tools// → //Options// → //Integration// tab and enabling //Use alternative R installation for Calculate R Expression//. |
| - | * A [[Lookup Table Type|Lookup Table]] requires //2// number vectors, one for the "Keys" and another for the "Values". Both vectors must have the same number of elements. | + | ==== Examples ==== |
| - | * The ''outputTable'' function requires a table, that can be construct with data.frame R function. Tt has an optional parameter too, to put how many Key columns the table has, from the leftmost column, the default value of it is 1. | + | The following examples use a consistent set of inputs: |
| - | <note important>Notice the use of the stringAsFactors = FALSE flag. This flag prevents R from converting the passed strings to Factors(numbers).</note> | + | |
| - | ==== Tables / Lookup Tables ==== | + | * ''t1'' — a lookup table of land cover patches, mapping ''Key'' (patch identifier) to ''Value'' (patch area) |
| + | * ''v1'' — a scalar minimum area threshold | ||
| + | * ''s1'' — a string giving the name to use for the threshold flag column | ||
| - | [[Table Type|Tables]] or [[Lookup Table Type|Lookup Tables]] can be transferred to R using the [[Number Table]] functor. When dealing with each of these types extra caution must be taken: | + | Compute the mean and total area across all patches, and report progress to the Message Log: |
| - | === Lookup Tables === | + | <code rsplus> |
| + | patchMean <- mean(t1$Value); | ||
| + | patchTotal <- sum(t1$Value); | ||
| + | print(paste("Processed", length(t1$Value), "patches")); | ||
| - | Lookup Tables are transferred to R as a List with two columns, "Keys" and "Values". | + | outputDouble("meanArea", patchMean); |
| + | outputDouble("totalArea", patchTotal); | ||
| + | </code> | ||
| - | Each column can be individually accessed by using the ''$'' operator, for example: | + | > **Note:** The ''print()'' call is visible in Dinamica EGO's Message Log, shown as a Result-level message. Log levels are ordered Unconditional, Error, Warning, Result, Info, Info2, Debug, Debug2 — messages printed from R are only shown when the Message Log level is set to Result or a more verbose level; at Unconditional, Error, or Warning they are suppressed. |
| - | * ''t1$Keys[ 1 ]'' will access the first key of the Lookup Table ''t1''. | + | Filter the lookup table down to patches whose area meets the threshold, and return the filtered lookup table: |
| - | + | ||
| - | * ''t2$Values[ 2 ]'' will access the second value of the Lookup Table ''t2'' | + | |
| - | === Tables === | + | <code rsplus> |
| + | aboveThreshold <- t1$Value >= v1; | ||
| + | filteredKeys <- t1$Key[aboveThreshold]; | ||
| + | filteredValues <- t1$Value[aboveThreshold]; | ||
| - | Tables are transferred to R as a [[https://en.wikibooks.org/wiki/R_Programming/Working_with_data_frames|DataFrame]] type variable. Each column can be individually accessed by using the ''$'' operator, just like Lookup Tables. Please check [[external_communication#table|this page]] for more information on how to deal with Tables. The rules for them in [[Calculate R Expression]] are the same as sending and receiving through [[External Communication]]. | + | outputLookupTable("filteredPatches", filteredKeys, filteredValues); |
| - | + | </code> | |
| - | ===== Example Scripts ===== | + | |
| - | + | ||
| - | ==== Multiply each value of the passed Lookup Table by itself ==== | + | |
| - | {{ ::ex1_calculate_r.png?nolink |}} | + | Build and return a table with one row per patch, including a column that flags whether each patch meets the threshold. The column's name is taken from the passed string ''s1'' rather than being hard-coded: |
| - | Expression: | ||
| <code rsplus> | <code rsplus> | ||
| - | for ( i in 1:length( t1$Values ) ) { | + | patchFlags <- data.frame( |
| - | tempValue <- t1$Values[ i ]; | + | PatchId = t1$Key, |
| - | t1$Values[ i ] = tempValue * tempValue; | + | Area = t1$Value, |
| - | } | + | MeetsThreshold = t1$Value >= v1, |
| + | stringsAsFactors = FALSE | ||
| + | ); | ||
| + | names(patchFlags)[3] <- s1; | ||
| - | outputLookupTable( "poweredTable", t1$Keys, t1$Values ); | + | outputTable("patchSummary", patchFlags, 1); |
| </code> | </code> | ||
| - | ==== Extract the mean of passed Lookup Table values ==== | + | Install the ''moments'' package and use it to compute the skewness of the patch area distribution — a statistic not available in base R — then flag patches whose area is a statistical outlier: |
| - | {{ ::ex2_calculate_r.png?nolink |}} | ||
| - | |||
| - | Expression: | ||
| <code rsplus> | <code rsplus> | ||
| - | tableMean <- mean( t1$Values ); | + | dinamicaPackage("moments"); |
| - | print( paste( "Mean is", tableMean ) ); | + | |
| - | outputDouble( "mean", tableMean ); | + | patchMean <- mean(t1$Value); |
| + | patchStdDev <- sd(t1$Value); | ||
| + | patchSkewness <- skewness(t1$Value); | ||
| + | |||
| + | isOutlier <- abs(t1$Value - patchMean) > 2 * patchStdDev; | ||
| + | outlierPatches <- data.frame( | ||
| + | PatchId = t1$Key[isOutlier], | ||
| + | Area = t1$Value[isOutlier], | ||
| + | stringsAsFactors = FALSE | ||
| + | ); | ||
| + | |||
| + | outputDouble("meanArea", patchMean); | ||
| + | outputDouble("stdDevArea", patchStdDev); | ||
| + | outputDouble("skewnessArea", patchSkewness); | ||
| + | outputTable("outlierPatches", outlierPatches, 1); | ||
| </code> | </code> | ||
| - | Noticed the ''print()'' statement? Dinamica EGO's Message Log will show output messages from the R script (as Result's). | + | ==== Writing the expression in EGO Script ==== |
| - | ==== Plot passed Lookup Table to an image on the path specified by passed String ==== | + | Like [[calculate_python_expression|Calculate Python Expression]], the ''Expression'' input is type [[ego_script#constants|Code]], which is represented in the underlying script using base64 encoding — impractical to write or edit directly as a text constant. Instead, connect a ''String'' carrier functor containing the R expression text to the ''Expression'' port; its output is accepted wherever a ''Code'' value is expected. Since only this one output is needed, the carrier can be [[ego_script#inline_syntax|inlined]] directly into the call. |
| - | {{ ::ex3_calculate_r.png?nolink |}} | + | This limitation is specific to hand-written EGO Script. In the Dinamica EGO GUI, the ''Expression'' port has a dedicated code editor that edits the ''Code'' value directly — the ''String'' carrier workaround is only necessary when writing or editing the ''.ego'' file as text. |
| - | Expression: | + | <code> |
| - | <code rsplus> | + | result := CalculateRExpression (String $"( |
| - | outputFile <- s1; | + | aboveThreshold <- t1$Value >= v1; |
| - | print( paste( "Plotting to", outputFile ) ); | + | outputLookupTable("filteredPatches", t1$Key[aboveThreshold], t1$Value[aboveThreshold]); |
| - | jpeg( outputFile, quality=100 ); | + | )") .no {{ |
| - | plot( t1 ); | + | NumberTable landCoverAreas 1; |
| - | dev.off(); | + | NumberValue minimumArea 1; |
| + | }}; | ||
| + | filteredPatches := ExtractStructLookupTable result "filteredPatches"; | ||
| </code> | </code> | ||
| + | |||
| + | ===== Group ===== | ||
| + | |||
| + | [[Functor List#Integration|Integration]] | ||
| ===== Internal Name ===== | ===== Internal Name ===== | ||
| CalculateRExpression | CalculateRExpression | ||
| - | |||