# science pack 50 nodes, in `noodlelab[science]` and above. ## Output (node-science.save_csv)= ### Save CSV `science.save_csv` Write a table into this run's output folder. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `filename` | `str` | Default `'table.csv'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Path` | | ## Science/Plot (node-science.bar_chart)= ### Bar Chart `science.bar_chart` One bar per row: a value per category, with optional error bars (e.g. mean ± standard error from Group Summary). **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `category` | `str` | Default `''`. | | `value` | `str` | Default `''`. | | `error` | `str` | Error bar column Default `''`. | | `horizontal` | `bool` | Default `False`. | | `title` | `str` | Default `''`. | | `y_label` | `str` | Default `''`. | | `color_by_sign` | `bool` | Negative bars in another colour Default `False`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Figure` | | (node-science.box_plot)= ### Box Plot `science.box_plot` Distribution per group: the box spans the quartiles, the line is the median, whiskers reach 1.5 IQR; individual values are drawn on top. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `value` | `str` | Default `''`. | | `group` | `str` | One box per group Default `''`. | | `show_points` | `bool` | Default `True`. | | `title` | `str` | Default `''`. | | `y_label` | `str` | Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Figure` | | (node-science.residual_plot)= ### Residual Plot `science.residual_plot` Residuals of a fit against x, with the zero line and a ±2σ band: patterns (curvature, funnels) mean the model misses something. **Inputs** | Name | Type | Description | |---|---|---| | `x` | `NDArray[number]` | | | `residuals` | `NDArray[number]` | | | `title` | `str` | Default `'Residuals'`. | | `x_label` | `str` | Default `'x'`. | | `y_label` | `str` | Default `'residual'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Figure` | | (node-science.scatter_plot)= ### Scatter Plot `science.scatter_plot` Two columns of a table against each other, optionally coloured by a third (numbers get a colour bar, categories a legend) and with a least-squares line. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `x` | `str` | Default `''`. | | `y` | `str` | Default `''`. | | `color` | `str` | Colour points by this column Default `''`. | | `fit_line` | `bool` | Default `False`. | | `colormap` | `Literal['viridis', 'plasma', 'cividis', 'magma', 'coolwarm', 'RdBu_r', 'YlOrRd', 'Blues']` | Default `'viridis'`. | | `title` | `str` | Default `''`. | | `x_label` | `str` | Default `''`. | | `y_label` | `str` | Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Figure` | | (node-science.time_series_plot)= ### Time Series Plot `science.time_series_plot` One or more columns against time; the first is drawn boldest. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `time` | `str` | Default `'date'`. | | `columns` | `str` | Comma-separated columns to draw Default `''`. | | `style` | `Literal['line', 'bars', 'steps']` | Default `'line'`. | | `title` | `str` | Default `''`. | | `y_label` | `str` | Default `''`. | | `zero_line` | `bool` | Default `False`. | | `color_by_sign` | `bool` | Bars above zero red, below blue (for anomalies) Default `False`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Figure` | | ## Science/Signal (node-science.butterworth_filter)= ### Butterworth Filter `science.butterworth_filter` A Butterworth filter applied forwards and backwards (zero phase shift, scipy.signal.sosfiltfilt): keep frequencies below, above, between or outside the cutoffs. **Inputs** | Name | Type | Description | |---|---|---| | `y` | `NDArray[number]` | | | `sample_rate` | `float` | Samples per second (Hz) Default `100.0`. | | `kind` | `Literal['lowpass', 'highpass', 'bandpass', 'bandstop']` | Default `'lowpass'`. | | `cutoff` | `float` | Hz; the lower edge for band filters Default `10.0`. | | `cutoff_high` | `float` | Hz; band filters only Default `20.0`. | | `order` | `int` | Default `4`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `NDArray[floating]` | | (node-science.detrend)= ### Detrend `science.detrend` Remove a straight-line trend (or only the mean, ``constant``), such as sensor drift, before computing spectra. **Inputs** | Name | Type | Description | |---|---|---| | `y` | `NDArray[number]` | | | `kind` | `Literal['linear', 'constant']` | Default `'linear'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `NDArray[floating]` | | (node-science.envelope)= ### Envelope `science.envelope` The amplitude envelope (magnitude of the analytic signal, via the Hilbert transform). The spectrum of the envelope of a band-passed signal reveals how often impacts repeat: the classic bearing-fault diagnosis. **Inputs** | Name | Type | Description | |---|---|---| | `y` | `NDArray[number]` | | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `NDArray[floating]` | | (node-science.fft)= ### Fft `science.fft` One-sided amplitude spectrum of a real signal. Fourier Transform (in Math/Fourier) does more: units, phase, windows, padding and the inverse. **Inputs** | Name | Type | Description | |---|---|---| | `y` | `NDArray[number]` | | | `sample_spacing` | `float` | Default `1.0`. | **Outputs** | Name | Type | Description | |---|---|---| | `frequency` | `NDArray[float64]` | | | `amplitude` | `NDArray[float64]` | | (node-science.find_peaks)= ### Find Peaks `science.find_peaks` Local maxima of y (scipy.signal.find_peaks), as a table with position, height, prominence and width at half prominence (in x units), sorted by prominence, largest first. **Inputs** | Name | Type | Description | |---|---|---| | `x` | `NDArray[number]` | | | `y` | `NDArray[number]` | | | `min_prominence` | `float` | How far a peak stands out Default `0.0`. | | `min_distance` | `int` | Samples between peaks Default `1`. | | `max_peaks` | `int` | Keep the most prominent N; 0: all Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `positions` | `NDArray[float64]` | | | `heights` | `NDArray[float64]` | | | `count` | `int` | | (node-science.savgol_filter)= ### Savitzky-Golay Filter `science.savgol_filter` Smooth a signal while preserving peak shape (scipy.signal.savgol_filter). **Inputs** | Name | Type | Description | |---|---|---| | `y` | `NDArray[floating]` | | | `window` | `int` | Default `21`. | | `order` | `int` | Default `3`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `NDArray[floating]` | | (node-science.signal_statistics)= ### Signal Statistics `science.signal_statistics` Root mean square, peak absolute value, peak-to-peak range, crest factor (peak / RMS: about 1.41 for a pure sine) and kurtosis (3 for Gaussian noise). Impacts, such as from a damaged bearing, raise the last two. **Inputs** | Name | Type | Description | |---|---|---| | `y` | `NDArray[number]` | | **Outputs** | Name | Type | Description | |---|---|---| | `rms` | `float` | | | `peak` | `float` | | | `peak_to_peak` | `float` | | | `crest_factor` | `float` | | | `kurtosis` | `float` | | | `summary` | `dict[str, float]` | | (node-science.welch_psd)= ### Welch PSD `science.welch_psd` Power spectral density by Welch's method: the average spectrum of overlapping windowed segments, much less noisy than a single FFT. ``density`` gives units²/Hz, ``spectrum`` units² (for reading tone amplitudes). **Inputs** | Name | Type | Description | |---|---|---| | `y` | `NDArray[number]` | | | `sample_rate` | `float` | Samples per second (Hz) Default `100.0`. | | `segment` | `int` | Samples per segment Default `1024`. | | `scaling` | `Literal['density', 'spectrum']` | Default `'density'`. | **Outputs** | Name | Type | Description | |---|---|---| | `frequency` | `NDArray[float64]` | | | `power` | `NDArray[float64]` | | ## Science/Statistics (node-science.bootstrap_ci)= ### Bootstrap CI `science.bootstrap_ci` A confidence interval for a statistic by resampling (percentile-BCa, scipy.stats.bootstrap), with no assumption about the distribution. **Inputs** | Name | Type | Description | |---|---|---| | `x` | `NDArray[number]` | | | `statistic` | `Literal['mean', 'median', 'std']` | Default `'mean'`. | | `confidence` | `float` | Default `0.95`. | | `resamples` | `int` | Default `5000`. | | `seed` | `int` | Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `estimate` | `float` | | | `low` | `float` | | | `high` | `float` | | (node-science.compare_groups)= ### Compare Groups `science.compare_groups` Do the groups differ? One-way ANOVA (or the rank-based Kruskal-Wallis test) across all groups, then every pair: Welch's t-test (or Mann-Whitney U), with Holm's correction for the number of comparisons. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `value` | `str` | The measured variable Default `''`. | | `group` | `str` | The column naming each row's group Default `''`. | | `test` | `Literal['anova', 'kruskal-wallis']` | Default `'anova'`. | | `alpha` | `float` | Default `0.05`. | **Outputs** | Name | Type | Description | |---|---|---| | `groups` | `DataFrame` | | | `pairs` | `DataFrame` | | | `statistic` | `float` | | | `p_value` | `float` | | | `effect_size` | `float` | | | `summary` | `dict[str, Any]` | | (node-science.correlation_matrix)= ### Correlation Matrix `science.correlation_matrix` Pairwise correlation coefficients between numeric columns. The first column, ``variable``, names the rows (Heatmap uses it as labels). **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `method` | `Literal['pearson', 'spearman', 'kendall']` | Default `'pearson'`. | | `columns` | `str` | Comma-separated; empty: every numeric column Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.detect_outliers)= ### Detect Outliers `science.detect_outliers` Flag unusual values in a column. ``iqr``: outside [Q1 − t·IQR, Q3 + t·IQR] (t = 1.5 is Tukey's rule); ``z-score``: more than t standard deviations from the mean; ``mad``: robust z-score from the median absolute deviation. Outputs the table with an ``outlier`` column, the table without them, and how many there were. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `column` | `str` | Default `''`. | | `method` | `Literal['iqr', 'z-score', 'mad']` | Default `'iqr'`. | | `threshold` | `float` | Default `1.5`. | **Outputs** | Name | Type | Description | |---|---|---| | `flagged` | `DataFrame` | | | `clean` | `DataFrame` | | | `count` | `int` | | (node-science.trend_test)= ### Trend Test `science.trend_test` Is there a monotonic trend? The Mann-Kendall test (non-parametric, robust to outliers) with Sen's slope, the median of all pairwise slopes, as is usual for climate and hydrology series. Dates are converted to decimal years, so with ``per`` = 10 the slope is per decade. The rows need not be in time order, and several values may share a time. ``table``: the input with Sen's line added as ``_trend``, for plotting. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `time` | `str` | Dates, or numbers such as years Default `''`. | | `value` | `str` | Default `''`. | | `per` | `float` | Express the slope per this many time units (years for dates) Default `10.0`. | | `alpha` | `float` | Default `0.05`. | **Outputs** | Name | Type | Description | |---|---|---| | `slope` | `float` | | | `intercept` | `float` | | | `tau` | `float` | | | `p_value` | `float` | | | `trend` | `str` | | | `table` | `DataFrame` | | | `summary` | `dict[str, Any]` | | (node-science.two_way_anova)= ### Two-Way ANOVA `science.two_way_anova` Analysis of variance with two factors, such as treatment and block in a randomised block trial: does each factor explain variation once the other is accounted for? Type II sums of squares (valid for unbalanced data); partial η² as the effect size. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `value` | `str` | The measured variable Default `''`. | | `factor_a` | `str` | e.g. the treatment Default `''`. | | `factor_b` | `str` | e.g. the block or site Default `''`. | | `interaction` | `bool` | Also test A × B (needs replicates) Default `False`. | **Outputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `p_a` | `float` | | | `p_b` | `float` | | | `summary` | `dict[str, Any]` | | ## Science/Studies (node-science.files_design)= ### Files Design `science.files_design` One row per file matching the pattern, so a Sweep zone processes each file in turn (column ``file``, and ``name`` without folder or suffix). **Inputs** | Name | Type | Description | |---|---|---| | `folder` | `Path` | Default `'data'`. | | `pattern` | `str` | Which files, e.g. *.csv or run_*/*.tif Default `'*.csv'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.grid_design)= ### Grid Design `science.grid_design` Every combination of evenly spaced values: ``levels`` per range (a normal parameter spans mean ± 2 sd), and each listed value. **Inputs** | Name | Type | Description | |---|---|---| | `parameters` | `str` | One per line: name = low .. high [unit], normal(mean, sd) or [a, b, c] Default `'g = 9.7 .. 9.9\nL = 0.9 .. 1.1 m'`. | | `levels` | `int` | Values per range Default `5`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.latin_hypercube)= ### Latin Hypercube `science.latin_hypercube` A space-filling random design: each parameter's range is cut into ``samples`` slices and each slice is used once (scipy.stats.qmc). **Inputs** | Name | Type | Description | |---|---|---| | `parameters` | `str` | One per line: name = low .. high [unit], normal(mean, sd) or [a, b, c] Default `'g = 9.7 .. 9.9\nL = 0.9 .. 1.1 m'`. | | `samples` | `int` | Default `50`. | | `seed` | `int` | The same seed gives the same design Default `1`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.morris_design)= ### Morris Design `science.morris_design` Morris elementary-effects screening (SALib): cheap, it tells which parameters matter at all. Runs (parameters + 1) × trajectories times. Analyse the sweep's results with Sensitivity Analysis. **Inputs** | Name | Type | Description | |---|---|---| | `parameters` | `str` | One per line: name = low .. high [unit], normal(mean, sd) or [a, b, c] Default `'g = 9.7 .. 9.9\nL = 0.9 .. 1.1 m'`. | | `trajectories` | `int` | Default `20`. | | `levels` | `int` | Default `4`. | | `seed` | `int` | The same seed gives the same design Default `1`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.random_design)= ### Random Design `science.random_design` Parameter sets drawn at random: uniformly within ranges, from normal distributions, or among listed values. **Inputs** | Name | Type | Description | |---|---|---| | `parameters` | `str` | One per line: name = low .. high [unit], normal(mean, sd) or [a, b, c] Default `'g = 9.7 .. 9.9\nk = normal(2, 0.1)'`. | | `samples` | `int` | Default `100`. | | `seed` | `int` | The same seed gives the same design Default `1`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.sensitivity_analysis)= ### Sensitivity Analysis `science.sensitivity_analysis` How much each parameter influences a result, from a Sweep zone over a Morris Design (μ*: mean absolute effect, σ: non-linearity and interactions) or a Sobol Design (S1: variance share alone, ST: with interactions, with 95 % confidence half-widths). **Inputs** | Name | Type | Description | |---|---|---| | `results` | `DataFrame` | | | `output` | `str` | The result column to explain Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `indices` | `DataFrame` | | | `chart` | `Any` | | (node-science.sobol_design)= ### Sobol Design `science.sobol_design` Sobol variance-based sensitivity (SALib, Saltelli's sampling): how much of the output's variance each parameter causes, alone (S1) and with its interactions (ST). Analyse the sweep's results with Sensitivity Analysis. **Inputs** | Name | Type | Description | |---|---|---| | `parameters` | `str` | One per line: name = low .. high [unit], normal(mean, sd) or [a, b, c] Default `'g = 9.7 .. 9.9\nL = 0.9 .. 1.1 m'`. | | `base` | `int` | A power of 2; runs = base × (parameters + 2) Default `256`. | | `seed` | `int` | The same seed gives the same design Default `1`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | ## Science/Tables (node-science.add_column)= ### Add Column `science.add_column` Compute a new column from others (pandas ``DataFrame.eval``). Supports + - * / ** , comparisons, pi and sqrt, exp, log, log10, sin, cos, tan and abs; put names with spaces in `back quotes`. Attribute access, indexing and other Python are refused. When columns have units, the arithmetic is done with them: the new column gets its unit, and adding metres to seconds is an error. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `name` | `str` | Default `'result'`. | | `expression` | `str` | Arithmetic on columns: length_cm / 100, or (a + b) / 2 Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.column_as_text)= ### Column As Text `science.column_as_text` The values of a column joined into one text, e.g. the names of the top three samples for a report sentence. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `column` | `str` | Default `''`. | | `separator` | `str` | Default `', '`. | | `max_items` | `int` | 0: all Default `0`. | | `format` | `str` | A format spec: .2f for numbers, %Y for dates; empty: as is Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `str` | | (node-science.describe)= ### Describe `science.describe` Descriptive statistics per column. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.describe_columns)= ### Describe Columns `science.describe_columns` Say what each column holds. The descriptions travel with the table and show in the inspector. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `descriptions` | `str` | column = what it holds, one per line Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.drop_missing)= ### Drop Missing `science.drop_missing` Remove rows with missing values (NaN or empty). **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `columns` | `str` | Only look at these columns; empty: all Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.filter_rows)= ### Filter Rows `science.filter_rows` Keep the rows matching a condition (pandas ``DataFrame.query``). The index is renumbered. The condition may use column names, constants, lists of constants (``region in ["North", "South"]``), arithmetic, comparisons, ``and``/``or``/``not`` and sqrt, exp, log, log10, sin, cos, tan and abs; attribute access, indexing and other Python are refused. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `condition` | `str` | A pandas query: magnitude >= 2.5 and region == "North". Put names with spaces in `back quotes`. Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.get_column)= ### Get Column `science.get_column` One column of a table as an array. Its type follows the column: numbers (NDArray[float64], or int64, which links anywhere floats do), text (NDArray[str_]), dates (NDArray[datetime64]) or true/false (NDArray[bool_]). **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `column` | `str` | | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `NDArray` | | (node-science.get_quantity_column)= ### Get Quantity Column `science.get_quantity_column` One numeric column as an array with its unit (see Set Column Units), so what follows converts and checks units. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `column` | `str` | | | `unit` | `str` | The column's unit, if the table does not give one Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Quantity` | | (node-science.group_summary)= ### Group Summary `science.group_summary` Summary statistics of one column for each group: one row per group, in the order the groups first appear. ``ci95`` is the half-width of the 95 % confidence interval of the mean (Student's t). **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `by` | `str` | The column that defines the groups Default `''`. | | `value` | `str` | The numeric column to summarise Default `''`. | | `statistics` | `str` | Comma-separated: count, mean, std, sem, ci95, min, median, max, sum Default `'count, mean, std, sem, min, max'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.join_tables)= ### Join Tables `science.join_tables` Combine two tables on a shared key column (a database join). Columns with the same name in both get the suffixes _left and _right. **Inputs** | Name | Type | Description | |---|---|---| | `left` | `DataFrame` | | | `right` | `DataFrame` | | | `on` | `str` | A column both tables have Default `''`. | | `how` | `Literal['left', 'inner', 'outer', 'right']` | Default `'left'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.load_csv)= ### Load CSV `science.load_csv` Read a CSV file into a table: from the workspace (relative paths) or from remote storage (e.g. lab-s3://2026/run1.csv). Headers such as "length [m]" give the column a unit, which it keeps through the table nodes. **Inputs** | Name | Type | Description | |---|---|---| | `path` | `FileRef` | | | `separator` | `str` | Default `','`. | | `units` | `bool` | Read "length [m]" or "length (m)" as a column "length" in metres Default `True`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.lookup_value)= ### Lookup Value `science.lookup_value` One cell: the value in ``value_column`` of the first row whose ``key_column`` equals ``key`` (compared as text), e.g. the mean yield of treatment "N120" from a Group Summary. Without a key column, the first row's value, e.g. after Sort Rows. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `key_column` | `str` | Empty: the first row Default `''`. | | `key` | `str` | The row whose key column holds this Default `''`. | | `value_column` | `str` | Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `Any` | | (node-science.make_table)= ### Make Table `science.make_table` Combine two arrays of the same length (numbers, text or dates) into a two-column table. **Inputs** | Name | Type | Description | |---|---|---| | `x` | `NDArray` | | | `y` | `NDArray` | | | `x_name` | `str` | Default `'x'`. | | `y_name` | `str` | Default `'y'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.parse_dates)= ### Parse Dates `science.parse_dates` Turn a text column into dates and times, for the time series nodes. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `column` | `str` | Default `'date'`. | | `format` | `str` | strftime format, e.g. %d.%m.%Y; empty: detect ISO dates Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.pivot_table)= ### Pivot Table `science.pivot_table` Cross-tabulate: one row per value of ``rows``, one column per value of ``columns``, cells aggregated from ``values``. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `rows` | `str` | Default `''`. | | `columns` | `str` | Default `''`. | | `values` | `str` | Default `''`. | | `aggregate` | `Literal['mean', 'sum', 'count', 'min', 'max', 'median']` | Default `'mean'`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.rename_columns)= ### Rename Columns `science.rename_columns` Give columns readable names, e.g. before adding a table to a report. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `mapping` | `str` | old=new pairs, comma-separated: temp_c=Temperature (°C) Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.select_columns)= ### Select Columns `science.select_columns` Keep only some columns, in the given order. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `columns` | `str` | Comma-separated, in the order wanted Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.set_column_units)= ### Set Column Units `science.set_column_units` Give columns their units. They stay with the table through filtering, sorting, grouping and joining, show in previews and report tables as "length (cm)", and make the table's column pins quantities. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `units` | `str` | column = unit, one per line or comma-separated: length_cm = cm Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.sort_rows)= ### Sort Rows `science.sort_rows` Sort by a column; with a limit, the top N rows (e.g. the largest events). **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `column` | `str` | Default `''`. | | `descending` | `bool` | Default `False`. | | `limit` | `int` | Keep the first N rows; 0: all Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.table_info)= ### Table Info `science.table_info` The number of rows and columns, e.g. for "n = 120" in a report. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | **Outputs** | Name | Type | Description | |---|---|---| | `rows` | `int` | | | `columns` | `int` | | ## Science/Time Series (node-science.climatology)= ### Climatology `science.climatology` The typical value for each month (or season, or day of the year) over a baseline period, and each row's anomaly: its departure from that normal. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `time` | `str` | Default `'date'`. | | `value` | `str` | Default `''`. | | `period` | `Literal['month', 'season', 'day of year']` | Default `'month'`. | | `baseline_start` | `int` | First year of the baseline; 0: all Default `0`. | | `baseline_end` | `int` | Last year of the baseline; 0: all Default `0`. | **Outputs** | Name | Type | Description | |---|---|---| | `climatology` | `DataFrame` | | | `anomalies` | `DataFrame` | | (node-science.resample)= ### Resample `science.resample` Aggregate to regular periods: daily to monthly means, hourly to daily sums. Each period is labelled by its start. Adds a ``count`` column with the number of values in each period. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `time` | `str` | Default `'date'`. | | `every` | `Literal['hour', 'day', 'week', 'month', 'quarter', 'year']` | Default `'month'`. | | `aggregate` | `Literal['mean', 'sum', 'min', 'max', 'median', 'count']` | Default `'mean'`. | | `columns` | `str` | Comma-separated; empty: every numeric column Default `''`. | | `min_count` | `int` | Periods with fewer values become empty (NaN) Default `1`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | | (node-science.rolling_window)= ### Rolling Window `science.rolling_window` Add a moving-window statistic of a column, e.g. a 7-day mean or a 5-year running average. Windows at the ends use the values they have. **Inputs** | Name | Type | Description | |---|---|---| | `table` | `DataFrame` | | | `column` | `str` | Default `''`. | | `window` | `int` | Rows per window Default `7`. | | `statistic` | `Literal['mean', 'median', 'sum', 'std', 'min', 'max']` | Default `'mean'`. | | `center` | `bool` | Default `True`. | | `name` | `str` | The new column; empty: _ Default `''`. | **Outputs** | Name | Type | Description | |---|---|---| | `result` | `DataFrame` | |