Browse the docs
Moving to Trinium

Moving from Python

Most of a data script reads, reshapes, filters and loads, and each of those steps has a Trinium node. This guide maps the common pandas and PySpark steps to nodes, and shows how a pipeline calls the code no node covers.

The ideas

In a scriptIn Trinium
The scriptPipeline: nodes wired together on a canvas. Every node runs at once and rows stream between them.
A function or a block of stepsNode.
A dataframe passed from step to stepThe rows on a wire. Every node shows its output columns as you build, before a run.
print() and a log fileThe Data, Metadata and Messages tabs under the canvas, and rows in and out for every node of every run.
The crontabSchedules, on a timer or when files arrive.
Credentials in a config file or .envConnections, encrypted and kept out of the pipeline.
Command-line argumentsParameters: values supplied when a pipeline runs.
Git history of the scriptVersions, Staging and Production.
Expressions in pandasOne SQL expression language, used in every node that takes a rule.

Step by step

Reading

TheirsTriniumNotes
pd.read_csv, spark.read.csvCSV Files, S3 FilesCSV Files for files you upload; the cloud file inputs read a folder of them from S3, Azure Blob, Google Cloud Storage, SFTP or a network share.
pd.read_parquet, spark.read.parquetParquet Files, S3 FilesA key=value/ folder becomes a column, and a filter on it skips the folders it rules out.
pd.read_excelExcel Files
pd.read_json, json_normalizeJSON Files, JSONJSON Files reads a file; JSON pulls fields out of a column that holds JSON text.
pd.read_sql, spark.read.jdbcPostgres input, MySQL input, SQL Server input, Snowflake inputOne input per database, each with a table picker or your own query. On Postgres, MySQL and SQL Server a bookmark reads only the rows past where the last successful run stopped.
requests in a loop over pagesREST API (Read)Page, offset, cursor and next-link paging, with the credential held on the connection.

Columns

TheirsTriniumNotes
df[cols], rename, astype, dropManage ColumnsKeep, rename, retype and describe columns in one place.
df.filter(regex=...), select by typeDynamic Select, Dynamic RenameColumns chosen by a rule, so a new column that matches is picked up without an edit.
assign, withColumn, np.whereFormula, Formula Multi-FieldEach new column is an expression; CASE WHEN … THEN … ELSE … END replaces np.where. Formula Multi-Field applies one expression to many columns.
str.strip, str.lower, str.replaceData Cleanse, Formula, Find & Replace
str.extract, str.contains with a patternRegEx
str.split, explodeText to Columns, JSONSplit into columns or into rows.
pd.to_datetime, strftimeDateTime

Rows

TheirsTriniumNotes
df[df.x > 0], query, whereFilterThe rows that fail the condition come out of their own anchor.
if / elif over a column, into several framesRouteUp to ten named branches, first match wins, and the rest go to Unmatched.
sort_values, orderBySort
drop_duplicates, dropDuplicatesUniqueThe duplicates come out of their own anchor.
fillna with a value, the mean or the medianFill MissingA forward fill is Formula Multi-Row with last_value([x]) IGNORE NULLS OVER (ORDER BY _row ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW).
head, tail, sampleSample
reset_index, monotonically_increasing_idRecord ID
range, date_rangeGenerate Rows

Combining

TheirsTriniumNotes
pd.merge, df.joinJoin, Join MultipleJoin's three anchors are the matched rows and each side's unmatched rows; wire them into a Union for a left or full outer join.
pd.concat, unionByNameUnionBy column name or by position.
pd.concat(axis=1)JoinJoin by record position.
a cross join with a small tableAppend Fields
rapidfuzz, fuzzywuzzyFuzzy Match

Reshaping and summarizing

TheirsTriniumNotes
groupby().agg(), groupBy().agg()Summarize
pivot_table, pivotCross Tab
melt, stackTranspose

Row by row

TheirsTriniumNotes
shift, rank, cumsum, rolling, groupby().transform()Formula Multi-RowWindow functions such as LAG, RANK and SUM(…) OVER (PARTITION BY …).
a loop that carries a running valueFormula Multi-RowRunning mode: _prev is the previous row's result, as in GREATEST(0, COALESCE(_prev, 0) + [received] - [shipped]).

Checking

TheirsTriniumNotes
assert, pandera, great_expectationsData ValidationFailing rows go to their own anchor, or the run stops.
df.describe, profilingData Profile
assert_frame_equal, df.compareData CompareEvery missing, extra and changed row, by key.

Writing

TheirsTriniumNotes
to_csv, to_parquet, to_jsonCSV File, Parquet File, JSON File, S3 File
to_excel with formattingExcel Report
to_sql, spark.write.jdbcPostgres output, MySQL output, SQL Server output, Snowflake outputAppend, replace or upsert. On Postgres, a failed load leaves no rows behind; on MySQL and SQL Server, switch on All or nothing for the same.
smtplibSend EmailOne message per row, per group or per run, with the rows attached.

Outside the pipeline

TheirsTriniumNotes
requests.get per rowAPI Lookup
a model, a library or code no node coversAPI Batch LookupPut it behind an HTTPS endpoint of your own; API Batch Lookup sends it the rows many at a time (see below).

Code no node covers

Some steps belong in code: a trained model, a library with no equivalent, a rule your team already maintains. Keep that code where you run code today, behind a small HTTPS endpoint: a function, a container or an internal service. API Batch Lookup sends it your rows, a hundred or more to a request, and adds each row's answer as new columns.

  • What it sends. {"rows": [row, row, ...]}, each row an object of its column values. The field name can be changed, so an existing model endpoint works as it is.
  • What comes back. A list with one answer per row, in the order sent, whose fields become new columns typed as you declare them.
  • What the endpoint does. You say whether it only calculates (a Test run calls it, so the preview shows real answers) or changes something (a Test run sends nothing).
  • When a call fails. The run stops naming the rows, or keeps them with empty answers and carries on, as you choose.
  • Credentials. The endpoint's address and credential live on a REST API connection, which sends them to that host only.

The function itself can be a few lines. The node's help has a Python AWS Lambda to start from. To build each request from one row's values instead, such as an id in the path, use API Lookup.

A step that only reshapes data is usually shorter as nodes than as a service: try the table above first.

Function names are from the public pandas API reference and PySpark API reference. Python is a trademark of the Python Software Foundation; pandas and Apache Spark are trademarks of their owners. Trinium is not affiliated with them.