Labware
Creating Labware from Tables
To plan liquid handling workflows, Pourfecto needs to know how Stocks are held in physical labware. As for stocks, Pourfecto uses CHESSCore to create Labware objects.
Pourfecto creates populated labware from stock tables with added labware columns. This suits source plates, destination plates, tubes, reservoirs, and other labware described in CSV files, spreadsheets, or DataFrames.
CHESSCore.df_to_labware — Function
df_to_labware(df::DataFrame, units::DataFrame; kwargs...) -> Vector{Labware}Reconstruct labware (and deposited well contents) from a dataframe representation.
df_to_labware expects labware metadata (labware, name, and well) plus the stock columns needed to reconstruct well contents. Stock content may be encoded in any stock dataframe format supported by df_to_stock.
Required columns in df
df must include the following columns:
labware:LocationKindname registered inlocation_kinds(e.g."WP96"), used to look up the kind and callbuild_location. Valid codes arekeys(location_kinds).name: Name/identifier of the labware instance. Multiple rows may share the samename(andlabware) to indicate multiple wells on the same physical item.well: Well identifier in row/column form (e.g."A1","H2","B12")
All remaining columns are treated as stock data and are passed to df_to_stock.
Behavior
- Creates one labware instance per unique
(labware, name)pair, viabuild_location(location_kinds[Symbol(labware)], name)– these are uncommitted (no database connection needed,location_idisnothing); commit the result yourself (e.g. viacommit_location!in CHESSDatabase) if you want it tracked. - Parses a
Stockper row from the stock columns usingdf_to_stock. - Looks up each row's well by name (
lw[well]), not by position.
Arguments
df::DataFrame: Labware + stock data (one row per filled well).units::DataFrame: Units/metadata table for the stock columns (passed through todf_to_stock).
See also
CHESSCore.labware_to_df — Function
labware_to_df(lws::Vector{<:Labware}, format::AbstractString = "vc"; kwargs...) -> (DataFrame, DataFrame)Convert labware into a dataframe representation suitable for serialization
The output contains one row per non-empty well and includes three labware descriptor columns plus the stock columns:
labware:string(kind(lw).name)– the registeredLocationKindname for the labware containing the well (nottypeof(lw); everyLabwareshares one concrete type, so the model/kind lives inkind(lw), not the Julia type).name:CHESSCore.name(lw)labware instance namewell:CHESSCore.name(w)well identifier (e.g.,"A1")
Stock content is converted using stock_to_df in the requested format. The returned units dataframe corresponds to the stock columns (not the labware/name/well columns).
Arguments
lws::Vector{<:Labware}: Labware objects to export.format::AbstractString = "vc": Stock encoding passed tostock_to_df.
See also
labware_to_df(lw::Labware, format::AbstractString = "vc"; kwargs...) -> (DataFrame, DataFrame)Convenience method for exporting a single Labware object. Equivalent to labware_to_df([lw], format; kwargs...).
See also: labware_to_df(::Vector{<:Labware})
Overview
A labware table is a stock table with three additional columns:
labwarenamewell
All other columns are interpreted as stock data and passed to df_to_stock.
labware = df_to_labware(df, units)The reverse operation is:
df, units = labware_to_df(labware)Required columns
The input dataframe must include:
| Column | Description |
|---|---|
labware | Name of a LocationKind registered in location_kinds, such as WP96. The plate kinds used on this page come from CHESSLabConstants, which is loaded with CHESS. See Registering Lab Constants. |
name | Name of the labware instance |
well | Well identifier, such as "A1", "B12", or "H2" |
For example:
using DataFrames
DataFrame(
labware = ["WP96","WP96","WP96"],
name = ["source_plate", "source_plate", "source_plate"],
well = ["A1", "A2", "A3"],
volume = [1000, 1000, 1000],
water = [1.0, 1.0, 1.0],
)The columns labware, name, and well give the location of each stock. The remaining columns describe the stock in that well.
Labware codes
The labware column holds the name of a LocationKind that CHESSCore knows. The available names are the keys of location_kinds:
keys(location_kinds)For each unique (labware, name) pair, Pourfecto builds one labware object with build_location(location_kinds[Symbol(labware_code)], name). Rows with the same labware code and name, such as "WP96" and "source_plate", are placed on the same object.
Example: create labware from a volume/concentration table
The following example creates a source plate with three filled wells.
using DataFrames
using Pourfecto
df = DataFrame(
labware = ["WP96","WP96","WP96"],
name = ["source_plate", "source_plate", "source_plate"],
well = ["A1", "A2", "A3"],
volume = [1000, 1000, 500],
water = [100, 80, 0],
ethanol = [0, 20, 100],
)
units = DataFrame(
volume = ["µL"],
water = ["percent"],
ethanol = ["percent"],
)
source_labware = df_to_labware(df, units)The stock data includes a volume column, so the stock portion is read as the "vc" volume/concentration format.
The result is a vector of labware objects. Here it holds one 96-well plate named "source_plate" with stocks in wells A1, A2, and A3.
Example: multiple labware objects in one table
A single dataframe can describe multiple pieces of labware.
df = DataFrame(
labware = ["WP96","WP96","WP96","WP96"],
name = ["source_plate_1", "source_plate_1", "source_plate_2", "source_plate_2"],
well = ["A1", "A2", "A1", "A2"],
volume = [1000, 1000, 500, 500],
water = [100, 50, 0, 25],
ethanol = [0.0, 50, 100, 75],
)
units = DataFrame(
volume = ["µL"],
water = ["percent"],
ethanol = ["percent"],
)
lws = df_to_labware(df, units)This creates two labware objects:
source_plate_1source_plate_2
Rows with the same (labware, name) pair are placed on the same labware object.
How df_to_labware works
df_to_labware performs these steps:
- Splits the table into labware metadata columns and stock columns.
- Creates one labware object for each unique
(labware, name)pair. - Parses the stock columns using
df_to_stock. - Converts each
welllabel, such as"A1", into a well index. - Deposits each parsed stock into the corresponding well.
Only the stock columns should appear in the units dataframe. The units dataframe should not include labware, name, or well.
Exporting labware to dataframes
Use labware_to_df to convert labware objects back into table form.
df, units = labware_to_df(source_labware)By default, this exports stock contents using the "vc" format.
df, units = labware_to_df(source_labware, "vc")To export using quantity format:
df, units = labware_to_df(source_labware, "q")The output dataframe contains one row per non-empty well.
Creating Labware Manually
Labware can also be built and filled directly with CHESSCore's build_location, location_kinds, and add_stock!. This suits examples, tests, and notebooks where a dataframe is unnecessary:
using Pourfecto, CHESSCore, Unitful
plate = build_location(location_kinds[:DeepWP96], "source_plate")
water_stock = 1u"mL" * string_to_component("water", Liquid)
add_stock!(plate, water_stock, 1, 1) # adds stock to row 1, column 1; well A1CHESSCore's Locations page describes location kinds, wells, and deposit!, which add_stock! uses.
Some Pourfecto examples set .stock directly, as in children(plate)[row, col].stock = ..., or add to it, as in well.stock += ..., instead of calling add_stock! or deposit!. This shortcut skips the well-capacity check of deposit!. It is appropriate when building a target composition that is known to fit. Use deposit! for labware whose existing contents are not known.