Create Datamaps for scattered csv files
This guide demonstrates how to create individual datamaps for each Assay based on CSV tables located within the dataset folder.
Use-case
Section titled “Use-case”Researchers often need to store specific metadata for given data entities. Examples for this include measurement units for a particular analyte or textual descriptions of the measured properties. In the context of an ARC, we store this information within datamaps. This workflow facilitates the automated generation of these datamaps for selected Assays. For each header we want to annotate, we define a Data Context, containing Explication, Object Type and a Unit. We then map over the data files in the given assays and create a Datamap according to the headers we encounter.
Procedure
Section titled “Procedure”- Load dependencies: Initialize and load the required ARC dependencies.
#r "nuget: ARCtrl.QueryModel, 3.0.0-alpha.4"open System.IOopen ARCtrlopen ARCtrl.QueryModel
let arcPath = @"H:\User\ARCs\BeechThinning"let arc = ARC.load arcPathlet arcTables = arc.ArcTables- Define ontology terms: Specify the ontology terms corresponding to the units and explications required for the datamap.
let µg_p_mg = OntologyAnnotation( name="microgram per milligram")let mg_p_smm = OntologyAnnotation( name="milligram per square millimiter")let unitless = OntologyAnnotation( name="none", tsr="NCIT", tan="http://purl.obolibrary.org/obo/NCIT_C41132")let floatType = OntologyAnnotation( name="float", tsr="NCIT", tan="http://purl.obolibrary.org/obo/NCIT_C48150")let maxQuantEfficiency = OntologyAnnotation( name="maximal quantum efficiency")let quantumYield = OntologyAnnotation( name="quantum yield", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0012004")let nonphotochemicalQuenching = OntologyAnnotation( name="nonphotochemical quenching", tsr="GO", tan="http://purl.obolibrary.org/obo/GO_0010196")let nonregulatedQuenching = OntologyAnnotation( name="nonregulated quenching")let specificeafWeight = OntologyAnnotation( name="specific leaf weight", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0001122")let sleafChloCo = OntologyAnnotation( name="leaf chlorophyll content", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0012002")let chloACo = OntologyAnnotation( name="chlorophyll a content", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0000293")let chloBCo = OntologyAnnotation( name="chlorophyll B content", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0000295")let chloRatio = OntologyAnnotation( name="chlorophyll ratio", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0000298")let carContent = OntologyAnnotation( name="carotenoid content", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0000496")let protPerLeafArea = OntologyAnnotation( name="Protein per leaf area")let protContent = OntologyAnnotation( name="protein content", tsr="TO", tan="http://purl.obolibrary.org/obo/TO_0000598")- Optional: Add or remove DataContext values according to your specific experimental requirements.
type AssayColumn = { Header : string Unit : OntologyAnnotation Description : string Explication : OntologyAnnotation ObjectType: OntologyAnnotation }type AssayDefinition = { AssayName : string Columns : AssayColumn list }- Define header names: Specify the exact column headers you intend to utilize as metadata labels.
let excelHeaders = [| "Specific_weight" "2:Y (II)" "2:Y (NO)" "2:Y (NPQ)" "2:Fv/Fm" "Chl_total" "Chl_a" "Chl_b" "Chl_a/b" "Car" |]- Configure Assay parameters: Define the target Assays alongside their associated metadata parameters, which include Headers (Labels), Units, Description, Explication, and ObjectType. (Note: If you modify the AssayColumn type, ensure these parameters are updated accordingly).
let assays = [ { AssayName = "Photosynthesis" Columns = [ { Header = "Fv/Fm" Unit = unitless Description = "maximal quantum efficiency" Explication = maxQuantEfficiency ObjectType = floatType } { Header = "Y(II)" Unit = unitless Description = "quantum yield" Explication = quantumYield ObjectType = floatType } { Header = "Y(NPQ)" Unit = unitless Description = "nonphotochemical quenching" Explication = nonphotochemicalQuenching ObjectType = floatType } { Header = "Y(NO)" Unit = unitless Description = "nonregulated quenching" Explication = nonregulatedQuenching ObjectType = floatType } ] } { AssayName = "Pigment content" Columns = [ { Header = "Chl_total" Unit = µg_p_mg Description = "Total chlorophyll content per leaf fresh weight" Explication = sleafChloCo ObjectType = floatType } { Header = "Chl_a" Unit = µg_p_mg Description = "Chlorophyll a content per leaf fresh weight" Explication = chloACo ObjectType = floatType } { Header = "Chl_b" Unit = µg_p_mg Description = "Chlorophyll b content per leaf fresh weight" Explication = chloBCo ObjectType = floatType } { Header = "Chl_a/b" Unit = unitless Description = "Carotenoid content per leaf fresh weight" Explication = chloRatio ObjectType = floatType } { Header = "Car" Unit = µg_p_mg Description = "Carotenoid content" Explication = carContent ObjectType = floatType } ] }
{ AssayName = "LeafBiomass" Columns = [ { Header = "Specific_weight" Unit = mg_p_smm Description = "Leaf fresh weight per leaf area" Explication = specificeafWeight // Hinweis: In deiner Definition fehlt hier das 'l' bei "specificLeafWeight" ObjectType = floatType } ] }
{ AssayName = "Total protein content" Columns = [ { Header = "Protein_per_Area" Unit = mg_p_smm Description = "Total leaf protein content per leaf area" Explication = protPerLeafArea ObjectType = floatType } { Header = "Protein_content" Unit = µg_p_mg Description = "Total leaf protein content per leaf fresh weight" Explication = protContent ObjectType = floatType } ] } ]- Optional: Perform any necessary string formatting or header corrections using the normalizeHeaders function.
// header correction functionlet normalizeHeader (header:string) = header .Trim() .Replace("2:", "") .Replace(" ", "")
// helper function for next steplet readHeaders (csvFile:string) = File.ReadLines(csvFile) |> Seq.head |> fun line -> line.Split(',') |> Array.map normalizeHeader- Generate datamaps: Execute the createDatamap function. This function iterates through the configured Assays and computationally generates the designated datamaps for each one.
let createDataContexts (arcPath:string) (assay:AssayDefinition) =
let datasetFolder = Path.Combine(arcPath, "assays", assay.AssayName, "dataset")
if Directory.Exists datasetFolder then
Directory.GetFiles(datasetFolder, "*.csv", SearchOption.AllDirectories) |> Array.collect (fun csvFile ->
printfn "analyse file: %s" csvFile
let headers = readHeaders csvFile
let relativePath = Path.GetRelativePath(arcPath, csvFile) .Replace("\\", "/")
let parentFolderName = Directory.GetParent(csvFile).Name
assay.Columns |> List.choose (fun col ->
let wantedHeader = normalizeHeader col.Header
headers |> Array.tryFindIndex ((=) wantedHeader) |> Option.map (fun i ->
let dataName = $"{relativePath}#col={i+1}"
printfn " -> Match found: %s (Column %i)" wantedHeader (i+1) DataContext( name = dataName, format = "text/csv", selectorFormat = "https://datatracker.ietf.org/doc/html/rfc7111", explication = col.Explication, unit = col.Unit, ObjectType = Some col.ObjectType, label = col.Header, description = col.Description
) ) ) |> List.toArray ) |> Array.toList else printfn "directory not found: %s" datasetFolder []
for assay in assays do let dataContexts = createDataContexts arcPath assay
printfn "%s: %i columns found" assay.AssayName dataContexts.Length
if not dataContexts.IsEmpty then
let datamap = Datamap(ResizeArray dataContexts) let isaAssay = arc.GetAssay(assay.AssayName) isaAssay.Datamap <- Some datamap
arc.Update(arcPath)