Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
calculate_r_expression [2026/07/19 21:35]
hermann
calculate_r_expression [2026/08/28 03:14] (current)
hermann Sync from local documentation review
Line 3: Line 3:
 ===== Description ===== ===== Description =====
  
-This is a **[[ego_script#​container_functors|container functor]]** that calls R externally ​and processes the script'​s outputs as if they were part of Dinamica EGO itself. Like the other calculator functors, data is connected through hook functors placed inside its ''<​nowiki>​{{ … }}</​nowiki>'' ​block.+This is a container functor that calls R externally ​with a user-defined expression. Like the other calculator functors, data is connected through hook functors placed inside its block.
  
 ===== Inputs ===== ===== Inputs =====
  
-^ Name ^ Type ^ Description ^ +^ Name  ^ Type  ^ Description ​ 
-| 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. | +| Expression ​ | [[Code ​Type]]  | The expression ​to run on R. Written directly as a Code constant using its own raw string syntax ​— see [[#​writing_the_expression_in_ego_script|Writing the expression in EGO Script]] below. ​ 
-| Treat Warning As Errors | [[Boolean Value Type]] | Warnings ​raised by the R script ​will be treated as errors. |+| Treat Warning As Errors ​ | [[Boolean Value Type]] ​ If true, warnings ​raised by the R script ​are treated as errors. ​ |
  
 ===== Optional Inputs ===== ===== Optional Inputs =====
Line 17: Line 17:
 ===== Outputs ===== ===== Outputs =====
  
-^ Name ^ Type ^ Description ^ +^ Name  ^ Type  ^ Description ​ 
-result ​| [[struct_type|Struct]] | A struct ​containing the output values generated by the expression. |+Result  ​| [[Struct ​Type]]  Struct ​containing the output values generated by the expression, one entry per call to an output function in the R script | 
 + 
 +===== Group ===== 
 + 
 +[[Functor List#​Integration | Integration]]
  
 ===== Notes ===== ===== Notes =====
Line 24: Line 28:
 ==== Expression inputs ==== ==== Expression inputs ====
  
-Data is passed into the expression through ​**hook** functors placed inside the container'​s ​''<​nowiki>​{{ … }}</​nowiki>'' ​block. Hooks can be added using the //Create a hook// button on the functor bar, or by dragging them in individually.+Data is passed into the expression through hook functors placed inside the container'​s block — the same 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.
  
-  * Tables and lookup tables → [[Number Table]] → available in R as ''​t1''​''​t2''​, …, ''​t100''​ +  * Tables and lookup tables → [[Number Table]] → available in R as t1, t2, …, t100 
-  * Scalar values → [[Number Value]] → available as ''​v1''​''​v2''​, …, ''​v100''​ +  * Scalar values → [[Number Value]] → available as v1, v2, …, v100 
-  * Strings → [[Number String]] → available as ''​s1''​''​s2''​, …, ''​s100''​+  * Strings → [[Number String]] → available as s1, s2, …, s100
  
-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.+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.
  
 === Tables and lookup tables === === Tables and lookup tables ===
Line 36: Line 40:
 Lookup tables and tables require extra care, since each is transferred to R using a different representation. Lookup tables and tables require extra care, since each is transferred to R using a different representation.
  
-**lookup table** is transferred as a list with two columns, ​''​Key'' ​and ''​Value''​. Each column is accessed with the ''​$'' ​operator:+A lookup table is transferred as a list with two columns, Key and Value. Each column is accessed with the $ operator:
  
 <code rsplus> <code rsplus>
 # Access the third key and its corresponding value in lookup table t1 # Access the third key and its corresponding value in lookup table t1
-patchId ​  ​<- t1$Key[ 3 ]; +patchId <- t1$Key[3];​ 
-patchArea <- t1$Value[ 3 ];+patchArea <- t1$Value[3];​
 </​code>​ </​code>​
  
-**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:+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:
  
-  ​* **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]])+  * Key columns are marked by an asterisk (*) appended to their column name — this is the same convention used throughout Dinamica EGO's table representation. A key column can be Real or String, the same as any other column
-  * R automatically converts string columns to **Factors** inside a ''​data.frame''​but Dinamica requires plain **Character Vectors**. Always build tables with ''​stringsAsFactors = FALSE''​ to prevent this conversion.+  * Each column'​s type is inferred from its data, the same rule that applies to any table: 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]].+For further detail on the underlying table representation,​ see [[External Communication]].
  
 ==== Expression outputs ==== ==== Expression outputs ====
  
-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:+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:
  
-^ Function ^ Output type ^ Example ^ +^ Function ​ ^ Output type  ^ Notes  ​^ Example ​ 
-''​outputDouble()'' ​| Real | ''​outputDouble("​myDouble",​ 3.14)'' ​+| outputDouble() ​ | Real  | Accepts any numeric value.  ​| outputDouble("​myDouble",​ 3.14)  
-''​outputNumberVector()'' ​| Tuple | ''​outputNumberVector("​myTuple",​ c(1:10))'' ​+| outputNumberVector() ​ | Tuple  | Accepts any collection of numbers.  ​| outputNumberVector("​myTuple",​ c(1:​10)) ​ 
-''​outputString()'' ​| String | ''​outputString("​myString",​ "This is a string"​)'' ​+| outputString() ​ | String ​ | Accepts any string value.  ​| outputString("​myString",​ "This is a string"​) ​ 
-''​outputLookupTable()'' ​| Lookup Table | ''​outputLookupTable("​myLUT",​ c(1:10), c(1:10) * 10)'' ​+| 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 | ''​outputTable("​myTable",​ data.frame(State = c("​Massachusetts",​ "​Massachusetts"​),​ City = c("​Boston",​ "​Chelsea"​),​ Population = c(667137, 39398), stringsAsFactors = FALSE), 2)'' ​|+| 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)  |
  
-Values passed to these functions can be constructed inline, as in the examples above, or supplied as variables.+==== Retrieving outputs ====
  
-  * Any numeric value can be passed to ''​outputDouble()''​. +Calculate R Expression returns a single ​[[Struct ​Type]] ​value (via its Result output port) containing every value passed to an output*() function. ​To retrieve individual values from that structuse the corresponding functor ​from the Integration group:
-  * Collections of numbers are valid number vectors for ''​outputNumberVector()''​. +
-  * A [[Lookup Table Type|lookup table]] requires two number vectors of equal length — one for the keys, one for the values. +
-  ​''​outputTable()''​ requires a table built with the [[http://​www.r-tutor.com/​r-introduction/​data-frame|data.frame]] ​function, using ''​stringsAsFactors = FALSE''​ as described aboveIts optional second parameter (default ''​1''​) sets how many leading columns, from the left, are key columns.+
  
-==== Retrieving outputs ====+^ 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() ​ |
  
-''​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 structuse the corresponding functor from the //​Integration//​ group:+Each functor takes two inputs: the Struct ​returned by Calculate R Expressionand the name of the entry to extract as a string constant.
  
-^ Functor ^ Retrieves ^ +==== Installing packages ====
-| [[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()''​ |+
  
-Each functor takes two inputs: the ''​Struct''​ returned ​by ''​CalculateRExpression''​and the name of the entry to extract as string constant.+Packages are installed ​by calling dinamicaPackage("​packageName"​) from within the expression — one call per package. Unlike [[Calculate Python Expression]]there is no separate input port for listing packages; dinamicaPackage() is the only mechanism available. 
 + 
 +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 single call. 
 + 
 +<code rsplus>​ 
 +dinamicaPackage("​moments"​);​ 
 +</​code>​
  
 ==== Setup ==== ==== Setup ====
  
-There are two ways to run R scripts from ''​CalculateRExpression''​.+There are two ways to run R scripts from Calculate R Expression — though only the local installation is available on Linux.
  
 === Dinamica EGO Enhancement Plugin === === Dinamica EGO Enhancement Plugin ===
  
-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.+Windows only. Download and install the [[Dinamica EGO Enhancement Plugin]]. It contains everything needed to run R scripts inside Dinamica EGO, with no further configuration.
  
 === Local R installation === === Local R installation ===
  
-Alternativelya local R installation can be used insteadThis requires:+On Linuxthis 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 pluginEither way, it requires:
  
-  * R version 3.3.1 or later. +  * R installed on the machine, with the Rscript executable (Rscript.exe ​on Windows) present ​in its bin sub-folder. 
-  * A valid ''​Rscript.exe''​ located ​in the R installation'​s ''​bin'' ​sub-folder. +  * The Dinamica package for R installed and at its latest version.
-  * The [[external_communication|Dinamica package]] for R installed and at its latest version.+
  
-To use a custom R installationgo to //Tools// → //Options// → //Integration// tab and enable //Use alternative R installation for Calculate R Expression//:+On Windowsthis 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".
  
-{{:​dinamica_options_use_alternative_r_install.png?​nolink|}}+Reports an error if this functor is used without either the Enhancement Plugin or a working local R installation configured.
  
-==== Examples ====+Raises an error if the number of Number Table hooks nested inside this container exceeds the capacity of the external communication message queue.
  
-The following examples use a consistent set of inputs:+==== Examples ====
  
-  * ''​t1''​ — a lookup table of land cover patchesmapping ​''​Key'' ​(patch identifier) to ''​Value'' ​(patch area) +The following examples use a consistent set of inputs: ​t1 is a lookup table of land cover patches mapping Key (patch identifier) to Value (patch area)v1 is a scalar minimum area threshold; and s1 is a string giving the name to use for threshold-flag column.
-  * ''​v1''​ — a scalar minimum area threshold +
-  * ''​s1''​ — a string giving the name to use for the threshold flag column+
  
 Compute the mean and total area across all patches, and report progress to the Message Log: Compute the mean and total area across all patches, and report progress to the Message Log:
  
 <code rsplus> <code rsplus>
-patchMean <- mean( t1$Value ); +patchMean <- mean(t1$Value);​ 
-patchTotal <- sum( t1$Value ); +patchTotal <- sum(t1$Value);​ 
-print( paste( "​Processed",​ length( t1$Value ), "​patches"​ ) );+print(paste("​Processed",​ length(t1$Value),​ "​patches"​));​
  
-outputDouble( "​meanArea",​ patchMean ); +outputDouble("​meanArea",​ patchMean);​ 
-outputDouble( "​totalArea",​ patchTotal );+outputDouble("​totalArea",​ patchTotal);​
 </​code>​ </​code>​
  
-> **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.+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.
  
 Filter the lookup table down to patches whose area meets the threshold, and return the filtered lookup table: Filter the lookup table down to patches whose area meets the threshold, and return the filtered lookup table:
Line 127: Line 131:
 <code rsplus> <code rsplus>
 aboveThreshold <- t1$Value >= v1; aboveThreshold <- t1$Value >= v1;
-filteredKeys <- t1$Key[ aboveThreshold ]; +filteredKeys <- t1$Key[aboveThreshold];​ 
-filteredValues <- t1$Value[ aboveThreshold ];+filteredValues <- t1$Value[aboveThreshold];​
  
-outputLookupTable( "​filteredPatches",​ filteredKeys,​ filteredValues );+outputLookupTable("​filteredPatches",​ filteredKeys,​ filteredValues);​
 </​code>​ </​code>​
  
-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:+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:
  
 <code rsplus> <code rsplus>
Line 142: Line 146:
     stringsAsFactors = FALSE     stringsAsFactors = FALSE
 ); );
-names( patchFlags )[ 3 ] <- s1;+names(patchFlags)[3] <- s1;
  
-outputTable( "​patchSummary",​ patchFlags, 1 );+outputTable("​patchSummary",​ patchFlags, 1);
 </​code>​ </​code>​
  
-==== Writing ​the expression ​in EGO Script ====+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:
  
-Like [[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. Insteadconnect a ''​String''​ carrier functor containing the R expression text to the ''​Expression''​ portits 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.+<code rsplus>​ 
 +dinamicaPackage("​moments"​);​ 
 + 
 +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>​ 
 + 
 +==== Writing ​the expression in EGO Script ====
  
-This limitation ​is specific to hand-written EGO ScriptIn 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.+Like [[Calculate Python Expression]],​ the Expression input is type [[Code Type]]It can be filled in directly as a text constantusing Code Type's own raw string syntax -- the same ''​$"<​delimiter>​( raw_characters )<​delimiter>"​'' ​form used by String constants. This is also the form the Dinamica EGO GUI'​s ​dedicated code editor ​generates when it writes ​the Expression port'value, so a hand-written script and one produced by the GUI take the same shapeSee [[Code Type]] for the full grammar, including the base64 alternative form.
  
 <​code>​ <​code>​
-result := CalculateRExpression ​(String ​$"(+result := CalculateRExpression $"(
 aboveThreshold <- t1$Value >= v1; aboveThreshold <- t1$Value >= v1;
-outputLookupTable( "​filteredPatches",​ t1$Key[ aboveThreshold ], t1$Value[ aboveThreshold ] ); +outputLookupTable("​filteredPatches",​ t1$Key[aboveThreshold],​ t1$Value[aboveThreshold]);​ 
-)".no {{+)" .no {{
     NumberTable landCoverAreas 1;     NumberTable landCoverAreas 1;
     NumberValue minimumArea ​   1;     NumberValue minimumArea ​   1;
Line 164: Line 188:
 </​code>​ </​code>​
  
-===== Group ===== +Here .no is the value of Treat Warning As Errors.
- +
-[[Functor List#​Integration|Integration]]+
  
 ===== Internal Name ===== ===== Internal Name =====
  
 CalculateRExpression CalculateRExpression
- 
-===== See Also ===== 
- 
-  * [[r_coupling|R Coupling]] 
-  * [[Calculate Python Expression]] 
-  * [[External Communication]] 
-  * [[ego_script#​container_functors|EGO Script — Container Functors]] 
-  * [[ego_script#​verbose_form|EGO Script — Verbose Form (Hooks)]] 
-  * [[calculate_functors|Calculate Functors — Complete Operator Documentation]] 
-