Labware
Creating Labware from Tables
To plan executable liquid handling workflows, Pourfecto needs information about how Stocks are contained in physical labware. Just like for Stock objects, Pourfecto uses JLIMS to create Labware objects.
Pourfecto can create populated labware objects from stock tables augmented with labware metadata. This is useful when source plates, destination plates, tubes, reservoirs, or other labware are described in CSV files, spreadsheets, or DataFrames.
Pourfecto.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: Labware type code used byPourfecto.generate(labware_code, name). Valid codes arekeys(labwares).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. - Parses a
Stockper row from the stock columns usingdf_to_stock.
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
Pourfecto.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(typeof(lw))for the labware containing the wellname:JLIMS.name(lw)labware instance namewell:JLIMS.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 | Labware type code used by Pourfecto.generate(labware, name) |
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],
)Here, the columns labware, name, and well describe where each stock is located. The remaining columns describe the stock in that well.
Labware codes
The labware column should contain a labware code recognized by Pourfecto.
keys(labwares)returns the available labware codes.
When parsing a table, Pourfecto calls:
Pourfecto.generate(labware_code, name)for each unique (labware, name) pair.
For example, rows with:
labware = "WP96"
name = "source_plate"will be placed on the same generated labware 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)Because the stock data includes a volume column, Pourfecto interprets the stock portion as the "vc" volume/concentration format.
The result is a vector of labware objects. In this example, the vector contains a single 96-well plate named "source_plate" with stocks deposited into 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
Internally, df_to_labware performs the following 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
In addition to building labware from tables with df_to_labware, you can create labware objects directly in Julia with generate.
This is useful for examples, tests, notebooks, and workflows where you want to programmatically construct source or target labware.
Pourfecto.generate — Function
generate(T::AbstractString, name::AbstractString)Generate a labware instance from a registered labware code.
T is a short identifier (index code) used to look up a labware template in the global labwares registry. If the code is present, this method delegates to the underlying generator:
generate(labwares[T], name)
Examples
lw = generate("WP96", "plate_1") # code must exist in keys(labwares)Adding stocks to labware
Pourfecto provides a convenience function for depositing a Stock into a specific well of a Labware object.
Pourfecto.add_stock! — Function
add_stock!(lw::JLIMS.Labware, stock::JLIMS.Stock, row::Integer, col::Integer) -> JLIMS.LabwareDeposit stock into the well at (row, col) in lw.
This is a convenience wrapper around JLIMS.deposit! for manually filling labware. If the selected well is not empty, a warning is emitted and the function still attempts to deposit the stock.
Arguments
lw::JLIMS.Labware: Labware object to modify.stock::JLIMS.Stock: Stock to deposit.row::Integer: Row index of the target well.col::Integer: Column index of the target well.
Returns
JLIMS.Labware: The modified labware objectlw.
This function selects the well at (row, col), deposits the given stock into that well, and returns the modified labware object.
For example:
using JLIMS
using Unitful
using Pourfecto
plate = generate("DeepWP96", "source_plate")
water_stock = 1u"mL" * chem"water"
add_stock!(plate, water_stock, 1, 1) # adds stock to row 1, column 1; well A1The returned object is the same labware object, modified in place:
Behavior when the well is not empty
If the selected well already contains a stock, add_stock! emits a warning and still attempts to deposit the new stock:
add_stock!(plate, another_stock, 1, 1)This may combine with or otherwise modify the existing well contents depending on the behavior of JLIMS.deposit!.
Example: manually fill a source plate
using JLIMS
using Unitful
using Pourfecto
source_plate = generate("DeepWP96", "source_plate")
stock_a = 1u"mL" * chem"water"
stock_b = 900u"µL" * chem"water" + 100u"µL" * chem"ethanol"
add_stock!(source_plate, stock_a, 1, 1) # A1
add_stock!(source_plate, stock_b, 1, 2) # A2