out2.mtr Turn raw cell wall thicknesses and lumen diameters into mean tracheidogram chronologies.
We represent the data hierarchy by a collection of nested association lists. In the raw data at the topmost level is the site list, following the pattern ((site-A-ID site-A-DATA) (site-B-ID site-B-DATA) (site-C-ID site-C-DATA)...) with the various site IDs being literal tags and the site data being further association lists, each of the pattern ((sample-X sample-X-DATA) (sample-Y sample-Y-DATA) ... ) where sample-X, sample-Y etc. are the sample identifiers and the corresponding sample data are more association lists, each of the form ((year-L year-L-DATA) (year-M year-M-DATA) (year-N year-N-DATA) ... ) with year-L, year-M, year-N... being the dates, as integers on an arbitrary timescale, and the data are replicate measurement association lists ((cell-file-P cell-file-P-DATA) (cell-file-Q cell-file-Q-DATA) ... ) where cell-file-P, cell-file-Q etc. are the identification numbers for the particular files of cells, and the cell file data are association lists of the form ((cell-N (wall-N lumen-N)) (cell-N-1 (wall-N-1 lumen-N-1) ... ). Later stages of processing build nested lists with different data at the lower levels, but keep the same kind of structure at the higher levels of the hierarchy.
Since we make extensive use of association lists, it makes sense to define some of the common operations.
Append the list <lis> to a reversed version of the list <st>. More or less equivalent to (append (reverse st) lis).
Generate a new updated version of the association list <a-list>. If (assoc item a-list) would recover the a pairing of <item> with some data, we replace this with a new pairing of <item> and the results of applying the function <update-op> to those data, but otherwise prepend a new entry associating <item> with the results of applying <update-op> to the empty list.
Merge new data with an existing association list <a-list>. Given the pair <a-item>, already a possible association list entry, use the <merge-op> function to merge it with any matching entry in the list, or otherwise add it.
Generate a modified version of the association list entry <a-item>. Use the function <proc-op> to transform the data in the cdr of the existing entry, keeping the car field unchanged.
Process the association list <a-list>, sorting the modified entries. Apply a processing function <proc-op> to the data in the cdr field of all the entries in the old list, maintaining the car field of the entries, but using this as a sort key to determine the order in the new list, through the comparison predicate <rel-op?>.
The files holding the cell size measurements required to poulate the nested association lists with data have a uniform stucture, simply a repeated sequence of five whitespace-separated values 1. a numeric sample code 2. a year and cell file code, consisting of 2.1. the date on an arbitrary timescale as an integer 2.2. the replicate measurement number, padded to three digits with zeros 3. the number giving the cell position within the measurement series 4. the double cell wall thickness measurement 5. the cell lumen diameter measurement. Negative values for the measurements denote missing observations, such as the double cell wall thickness for the first cells in the series, or the lumen diameter for the last cells in the series: by convention each series begins after the common cell wall of the last latewood cell of the previous ring and ends with the thickness of the common cell wall of the last latewood cell of the current ring and the first earlywood cell of the next ring. The original log files from the cell measurement add-ons to the NIH Image program also add repeated headers, the sequence of five labels "Sample File N Wall Lumen".
Read a sequence of five values from a data file. Given <in-obj>, a pair consisting of a list of previously read data values and an input port, generate an updated version with the next sequence of five values read from the file and prepended to the list. A premature end-of-file condition may leave the sequence incomplete.
Read measurements from the file named by the string <fname>. The new data are merged with any already present in the collection of nested association lists, <top-a-list>, using the site identification literal <site-id>. Missing measurements get recoded from negative numbers to a literal value: missing.
Ideally for each cell we would have a lumen diameter and two cell wall measurements, from the lumen to the middle lamella between it and the previous cell along the radial file, and the corresponding width to the middle lamella dividing it from the next cell; however in practice the middle lamellae are often obscure, so we have to make do with measurements of the distance between lumena instead: the doule cell wall thickness. Because this single measurement includes contributions from two distinct cells, we apply some crude heuristics to partition it into two components for the purposes of computing cell sizes. With some exceptions, a cell size will depend on the observed lumen diameter, and a wall thickness that depends on a partial contribution from the preceding and following double cell wall measurements. For convenience with subsequent processing, as soon as we have derived the lists of cell sizes and wall thicknesses we turn them into vectors.
Turn lists of wall measurements <wall> and cell sizes <cell> into vectors.
Turn raw lumen diameters and double cell wall thicknesses into cell sizes. Given <raw> a list of unprocessed raw measurements of the form ((current-cell-wall current-lumen) (next-cell-wall next-lumen) ...) <prev>, the previously processed raw measurement as the list (previous-double-cell-wall previous-lumen) <wall>, a stack of accumulated corrected wall thicknesses and <cell>, a stack of accumulated cell sizes, we recurse down the list of of unprocessed measurements, maintaining the corrected measurement lists, and finally return them as a list of two vectors. In the simplest case the corrected cell size would be simply the sum of the lumen diameter and the mean of the preceding and following double cell wall thicknesses, but missing values and end cases require a case analysis and some crude approximations.
Build a collection nested association lists holding cell and wall sizes. From <obs-a-list>, the nested association lists containing the raw observed lumen diameters and double cell wall thicknesses, we construct the corresponding collection of lists holding vectors of corrected cell size and wall dimensions.
For a given year at a particular site the rings in different samples, and even the replicate measurements within a ring, will generally have different numbers of cells within the radial files of tracheids. To produce a single set of measurements summarizing the pattern of cell sizes for that ring at that site, the mean tracheidogram, we must coerce the measurement series to all have the same length. Here we use the same simple weighted averaging technique that the Institue of Forest have used for many years.
Build a collection of site-specific annual cell number totals. Given <size-a-list>, the collection of nested association lists that contains the corrected cell size and wall thickness vectors, we generate a new association list pairing site IDs with chronologies of cell file totals. These chronologies are unsorted association lists where each entry is of the form (date total-number-of-cell-files . total-number-of-cells) with the totals accumulated over all the replicated cell files for all the samples at a site. Trivially, the ratio of the totals gives the mean number of cells in the radial file of tracheids for a particular date at a particular site.
Make a list of length <n> from an arbitrary-length measurement vector <c>. In real conifer wood the number of cells in a radial file of tracheids will vary, not only from tree to tree and ring to ring, but also between different files of cells within a ring. We must account for these variations in length to turn multiple series of observed cell measurements into summaries, and the simplest way to do this, long used at the Institute of Forest, uses weighted means to map the observations onto a fixed-length vector.
Generate the mean tracheidogram for a particular year. Given a cell-file length, <n>, a number giving the date of the ring on whatever arbitrary time-scale is in use, <yr-date>, and nested association lists holding the corrected cell wall and total cell sizes, <measurements>, we extract all the size data from all the samples and replicated cell files for that date, standardize each individual cell file to have a length of <n>, accumulate them into a running totals of standardized cell measurements, and finally divide by the total number of cell files for the mean tracheidograms. Two lists represent the tracheidograms, one of standardized cell wall thicknesses and the other of standardized total cell sizes, and we redundantly associate them with the length used in the standardization.
Build a chronology of mean tracheidograms for a single site. The association list <yrlist> must already contain entries pairing the date of each year in the chronology with the total number of cell files measured for that year at that site and the sum of these cell file lengths; <measurelist> must contain the corresponding cell size and wall thickness data within nested association lists (cell-file replicates within years within samples). We build up a sequence of mean tracheidograms for each year, each standardized to the mean cell-file length for that year, keeping it sorted by the dates for the years.
Make chronologies of mean tracheidograms for all sites. The collection nested association lists, <measurements>, should contain corrected cell size and wall thickness data, with measurements of replicated cell files nested within dated rings, in turn nested within samples, which are nested within the site list entries. The list of chronologies is another nested association list, associating each year with a mean cell file length, and two lists of cell size and wall thickness values, both standardized to this length, then pairing the sorted list of years with the relevant site ID.
Insert placeholders for missing years. The association list <chron-yrs> represents part of a mean tracheidogram chronology, each entry pairing dates with the mean cell file lengths and lists of mean values; the association list <ex-yrs> is the stack of already processed results. If the next chronology entry does not have the date immediately following that of the entry at the top of the stack, we insert placeholders for the missing years, with zero lengths and null lists of mean values.
Tabulate the mean cell file lengths. Given a list of mean cell file lengths, <todo>, a stack of previously tabulated rows, <tabulated>, and stack of partially completed column values, <buffer>, we push the next length onto the row buffer and update the stacks when the row and table lengths are reached.
Tabulate the mean cell dimension values. We build up the table from a list of value lists, one list of values for each year. The <seen> list holds the stack of lists that have already been processed for the current table row, <unseen> those yet to be processed, <table> the stack of already assembled table rows, <row> the stack of current row values, and <valid> a count of the number of valid values encountered so far in this row.
Tabulate the start year, number of years, and mean values for one site. The association list <chron> holds the mean chronology data, pairing dates with mean tracheidograms represented as lists of mean wall thickness and cell size values. We ignore the wall thicknesses, and only tabulate the cell sizes at the moment.
The formatting needs specific field sizes, hence a Common Lisp-style format.
Write a single site to a single .mtr file.
Write all the sites to their individual .mtr files.
Read all the input files, write all the output files.
Turn lists of wall measurements <wall> and cell sizes <cell> into vectors.
(define makevec (lambda (wall cell) (list (list->vector (reverse wall)) (list->vector (reverse cell)))))
Turn raw lumen diameters and double cell wall thicknesses into cell sizes. Given <raw> a list of unprocessed raw measurements of the form ((current-cell-wall current-lumen) (next-cell-wall next-lumen) ...) <prev>, the previously processed raw measurement as the list (previous-double-cell-wall previous-lumen) <wall>, a stack of accumulated corrected wall thicknesses and <cell>, a stack of accumulated cell sizes, we recurse down the list of of unprocessed measurements, maintaining the corrected measurement lists, and finally return them as a list of two vectors. In the simplest case the corrected cell size would be simply the sum of the lumen diameter and the mean of the preceding and following double cell wall thicknesses, but missing values and end cases require a case analysis and some crude approximations.
(define corr-measures (lambda (raw prev wall cell) (cond ((null? raw) (if (or (null? prev) (eq? (car prev) 'missing) (eq? (cadr prev) 'missing)) (makevec wall cell) (makevec (cons (car prev) wall) (cons (+ (car prev) (cadr prev)) cell)))) ((or (null? prev) (eq? (cadr prev) 'missing)) ; No previous lumen (corr-measures (cdr raw) (car raw) wall cell)) ((eq? (car prev) 'missing) ; No previous wall (if (eq? (caar raw) 'missing) ; No current wall (corr-measures (cdr raw) (car raw) wall cell) (corr-measures (cdr raw) ; Use current wall (car raw) (cons (caar raw) wall) (cons (+ (caar raw) (cadr prev)) cell)))) ((eq? (caar raw) 'missing) ; No current wall (corr-measures (cdr raw) (car raw) (cons (car prev) wall) (cons (+ (car prev) (cadr prev)) cell))) (else (let ((avg-wall (if (eq? (cadar raw) 'missing) ; No current lumen (+ (/ (car prev) 2) (caar raw)) (/ (+ (car prev) (caar raw)) 2)))) (corr-measures (cdr raw) (car raw) (cons avg-wall wall) (cons (+ avg-wall (cadr prev)) cell)))))))
Build a collection nested association lists holding cell and wall sizes. From <obs-a-list>, the nested association lists containing the raw observed lumen diameters and double cell wall thicknesses, we construct the corresponding collection of lists holding vectors of corrected cell size and wall dimensions.
(define correct-walls (lambda (obs-a-list) (letrec ((corr-sites (lambda (done todo) (cond ((null? todo) done) ((eq? (caar todo) 'err) (corr-sites (cons (car todo) done) (cdr todo))) (else (corr-sites (cons (process-tag (car todo) corr-samp) done) (cdr todo)))))) (corr-samp (lambda (samp-list) (sort-map samp-list < corr-year))) (corr-year (lambda (year-list) (sort-map year-list < corr-cfile))) (corr-cfile (lambda (cfile-list) (sort-map cfile-list < corr-cell))) (corr-cell (lambda (measures) (corr-measures (map cdr (sort-map measures < car)) '() '() '())))) (corr-sites '() obs-a-list))))
The formatting needs specific field sizes, hence a Common Lisp-style format.
(require-extension format)
Write a single site to a single .mtr file.
(define write-mtr-file (lambda (site-id site-chron) (with-output-to-file (string-append site-id ".mtr") (lambda () (format #t "~{~{~4D~}~%~{~6{~12D~}~&~}~{~{~6,1F ~}~&~}~}" (tabulate-mean-chronology site-chron))))))
Write all the sites to their individual .mtr files.
(define write-mtr (lambda (meanchron) (for-each (lambda (site-entry) (write-mtr-file (car site-entry) (cdr site-entry))) meanchron)))
(require-extension utils)
Read all the input files, write all the output files.
(define process-args (lambda (arglist raw-chron) (if (null? arglist) (write-mtr (make-mean-chronologies (correct-walls raw-chron))) (let* ((arg (car arglist)) (chronid (call-with-values (lambda () (decompose-pathname arg)) (lambda (dir base ext) base)))) (if (not chronid) (process-args (cdr arglist) raw-chrons) (process-args (cdr arglist) (merge-obs raw-chron chronid arg)))))))
(process-args (cdr (argv)) '())
Append the list <lis> to a reversed version of the list <st>. More or less equivalent to (append (reverse st) lis).
(define restore (lambda (st lis) (if (null? st) lis (restore (cdr st) (cons (car st) lis)))))
Generate a new updated version of the association list <a-list>. If (assoc item a-list) would recover the a pairing of <item> with some data, we replace this with a new pairing of <item> and the results of applying the function <update-op> to those data, but otherwise prepend a new entry associating <item> with the results of applying <update-op> to the empty list.
(define update-assoc (lambda (item a-list update-op) (letrec ((update-cycle (lambda (a-nonmatch a-residue) (cond ((null? a-residue) (cons (cons item (update-op '())) (reverse a-nonmatch))) ((equal? item (caar a-residue)) (cons (cons item (update-op (cdar a-residue))) (restore a-nonmatch (cdr a-residue)))) (else (update-cycle (cons (car a-residue) a-nonmatch) (cdr a-residue))))))) (update-cycle '() a-list))))
Merge new data with an existing association list <a-list>. Given the pair <a-item>, already a possible association list entry, use the <merge-op> function to merge it with any matching entry in the list, or otherwise add it.
(define a-merge (lambda (a-item a-list merge-op) (update-assoc (car a-item) a-list (lambda (info) (merge-op (cdr a-item) info)))))
Generate a modified version of the association list entry <a-item>. Use the function <proc-op> to transform the data in the cdr of the existing entry, keeping the car field unchanged.
(define process-tag (lambda (a-item proc-op) (cons (car a-item) (proc-op (cdr a-item)))))
Process the association list <a-list>, sorting the modified entries. Apply a processing function <proc-op> to the data in the cdr field of all the entries in the old list, maintaining the car field of the entries, but using this as a sort key to determine the order in the new list, through the comparison predicate <rel-op?>.
(define sort-map (lambda (a-list rel-op? proc-op) (letrec ((insert-sort (lambda (item dest) (letrec ((insert-help (lambda (before after) (cond ((null? before) (restore after (list (process-tag item proc-op)))) ((rel-op? (caar before) (car item)) (insert-help (cdr before) (cons (car before) after))) (else (restore after (cons (process-tag item proc-op) before))))))) (insert-help dest '())))) (map-help (lambda (todo done) (if (null? todo) done (map-help (cdr todo) (insert-sort (car todo) done)))))) (map-help a-list '()))))
Read a sequence of five values from a data file. Given <in-obj>, a pair consisting of a list of previously read data values and an input port, generate an updated version with the next sequence of five values read from the file and prepended to the list. A premature end-of-file condition may leave the sequence incomplete.
(define extract-item (lambda (in-obj) (letrec ((give-me-n (lambda (st n fport) (if (= n 0) (cons (reverse st) fport) (let ((nth (read fport))) (if (eof-object? nth) (cons nth (close-input-port fport)) (give-me-n (cons nth st) (- n 1) fport))))))) (give-me-n '() 5 (cdr in-obj)))))
Read measurements from the file named by the string <fname>. The new data are merged with any already present in the collection of nested association lists, <top-a-list>, using the site identification literal <site-id>. Missing measurements get recoded from negative numbers to a literal value: missing.
(define merge-obs (lambda (top-a-list site-id fname) (letrec ((extract-input (lambda (in-obj) (let ((new-input (extract-item in-obj))) (cond ((eof-object? (car new-input)) new-input) ((equal? (car new-input) '(Sample File N Wall Lumen)) (extract-input new-input)) (else (cons (decode-item (car new-input)) (cdr new-input))))))) (decode-item (lambda (item) (if (and (list? item) (= (length item) 5)) (let ((year-file (cadr item)) (cell-num (caddr item)) (raw-wall (cadddr item)) (raw-lumen (car (cddddr item)))) (if (and (integer? year-file) (integer? cell-num) (real? raw-wall) (real? raw-lumen)) (list (car item) (quotient year-file 1000) (remainder year-file 1000) cell-num (if (>= raw-wall 0) raw-wall 'missing) (if (>= raw-lumen 0) raw-lumen 'missing)) (cons 'err (cons 'in-values (cons fname item))))) (cons 'err (cons 'in-format (cons fname item)))))) (merge-cell (lambda (measure cellinfo) (a-merge measure cellinfo cons))) (merge-cfile (lambda (cell cfileinfo) (a-merge cell cfileinfo merge-cell))) (merge-year (lambda (cfile yearinfo) (a-merge cfile yearinfo merge-cfile))) (merge-input (lambda (in-obj sampinfo) (let ((next-input (extract-input in-obj))) (if (eof-object? (car next-input)) sampinfo (merge-input next-input (a-merge (car next-input) sampinfo merge-year))))))) (update-assoc site-id top-a-list (lambda (sampinfo) (merge-input (cons '() (open-input-file fname)) sampinfo))))))
Build a collection of site-specific annual cell number totals. Given <size-a-list>, the collection of nested association lists that contains the corrected cell size and wall thickness vectors, we generate a new association list pairing site IDs with chronologies of cell file totals. These chronologies are unsorted association lists where each entry is of the form (date total-number-of-cell-files . total-number-of-cells) with the totals accumulated over all the replicated cell files for all the samples at a site. Trivially, the ratio of the totals gives the mean number of cells in the radial file of tracheids for a particular date at a particular site.
(define total-cell-counts (lambda (size-a-list) (letrec ((total-cfile-n (lambda (n-totals cfile-list) (if (null? cfile-list) n-totals (total-cfile-n (cons (+ 1 (car n-totals)) (+ (vector-length (caddar cfile-list)) (cdr n-totals))) (cdr cfile-list))))) (total-annual-n (lambda (yr-totals sample-yr) (if (null? sample-yr) yr-totals (let ((cfile-totals (total-cfile-n '(0 . 0) (cdr sample-yr)))) (update-assoc (car sample-yr) yr-totals (lambda (old-total) (if (null? old-total) cfile-totals (cons (+ (car cfile-totals) (car old-total)) (+ (cdr cfile-totals) (cdr old-total)))))))))) (total-sample-n (lambda (yr-totals yr-list) (if (null? yr-list) yr-totals (total-sample-n (total-annual-n yr-totals (car yr-list)) (cdr yr-list))))) (total-site-n (lambda (yr-totals samples) (if (null? samples) yr-totals (total-site-n (total-sample-n yr-totals (cdar samples)) (cdr samples))))) (total-site-yr-n (lambda (sample-list) (total-site-n '() sample-list))) ) (sort-map size-a-list string<? total-site-yr-n))))
Make a list of length <n> from an arbitrary-length measurement vector <c>. In real conifer wood the number of cells in a radial file of tracheids will vary, not only from tree to tree and ring to ring, but also between different files of cells within a ring. We must account for these variations in length to turn multiple series of observed cell measurements into summaries, and the simplest way to do this, long used at the Institute of Forest, uses weighted means to map the observations onto a fixed-length vector.
(define standadize-measurements (lambda (c n) (letrec ((nc (vector-length c)) (interp (lambda (t i j) (letrec ((interp-help (lambda (m j s) (if (<= m (* n (+ j 1))) (interp (cons (/ (+ s (* (vector-ref c j) (- m (* n j)))) nc) t) (+ i 1) (+ j 1)) (interp-help m (+ j 1) (+ s (* n (vector-ref c j)))))))) (cond ((>= i n) (reverse t)) ((<= (* n j) (* nc i)) (interp t i (+ j 1))) ((>= (* n j) (* nc (+ i 1))) (interp (cons (vector-ref c (- j 1)) t) (+ i 1) j)) (else (interp-help (* nc (+ i 1)) j (* (vector-ref c (- j 1)) (- (* n j) (* nc i)))))))))) (interp '() 0 0))))
Generate the mean tracheidogram for a particular year. Given a cell-file length, <n>, a number giving the date of the ring on whatever arbitrary time-scale is in use, <yr-date>, and nested association lists holding the corrected cell wall and total cell sizes, <measurements>, we extract all the size data from all the samples and replicated cell files for that date, standardize each individual cell file to have a length of <n>, accumulate them into a running totals of standardized cell measurements, and finally divide by the total number of cell files for the mean tracheidograms. Two lists represent the tracheidograms, one of standardized cell wall thicknesses and the other of standardized total cell sizes, and we redundantly associate them with the length used in the standardization.
(define take-means (lambda (n yr-date measurements) (letrec ((init-totals (lambda (i initlist) (if (zero? i) initlist (init-totals (- i 1) (cons 0 initlist))))) (initial-totals (list 0 (init-totals n '()) (init-totals n '()))) (update-totals (lambda (cfile runningtotals) (let* ((running-count (car runningtotals)) (running-walls (cadr runningtotals)) (running-sizes (caddr runningtotals)) (std-walls (standadize-measurements (car cfile) n)) (std-sizes (standadize-measurements (cadr cfile) n))) (list (+ 1 running-count) (map + std-walls running-walls) (map + std-sizes running-sizes))))) (cfile-iterate (lambda (cfile-list accum-total) (if (null? cfile-list) accum-total (cfile-iterate (cdr cfile-list) (update-totals (cdar cfile-list) accum-total))))) (samp-iterate (lambda (samp-list accum-total) (if (null? samp-list) accum-total (samp-iterate (cdr samp-list) (cfile-iterate (cdr (assoc yr-date (cdar samp-list))) accum-total))))) (final-totals (samp-iterate measurements initial-totals))) (cons yr-date (let* ((final-count (car final-totals)) (final-walls (cadr final-totals)) (final-sizes (caddr final-totals)) (total-to-mean (lambda (tot) (/ tot final-count)))) (list n (map total-to-mean final-walls) (map total-to-mean final-sizes)))))))
Build a chronology of mean tracheidograms for a single site. The association list <yrlist> must already contain entries pairing the date of each year in the chronology with the total number of cell files measured for that year at that site and the sum of these cell file lengths; <measurelist> must contain the corresponding cell size and wall thickness data within nested association lists (cell-file replicates within years within samples). We build up a sequence of mean tracheidograms for each year, each standardized to the mean cell-file length for that year, keeping it sorted by the dates for the years.
(define chron-build (lambda (yrlist measurelist) (letrec ((insert-yr (lambda (yr-date yr-cftotals dest) (letrec ((means (take-means (inexact->exact (round (/ (cdr yr-cftotals) (car yr-cftotals)))) yr-date measurelist)) (insert-iterate (lambda (before after) (cond ((null? before) (restore after (list means))) ((< (caar before) yr-date) (insert-iterate (cdr before) (cons (car before) after))) (else (restore after (cons means before))))))) (insert-iterate dest '())))) (yr-iterate (lambda (todo done) (if (null? todo) done (yr-iterate (cdr todo) (insert-yr (caar todo) (cdar todo) done)))))) (yr-iterate yrlist '()))))
Make chronologies of mean tracheidograms for all sites. The collection nested association lists, <measurements>, should contain corrected cell size and wall thickness data, with measurements of replicated cell files nested within dated rings, in turn nested within samples, which are nested within the site list entries. The list of chronologies is another nested association list, associating each year with a mean cell file length, and two lists of cell size and wall thickness values, both standardized to this length, then pairing the sorted list of years with the relevant site ID.
(define make-mean-chronologies (lambda (measurements) (letrec ((totals (total-cell-counts measurements)) (site-mean (lambda (sitelist chronologies) (if (null? sitelist) (reverse chronologies) (letrec ((site-id (caar sitelist)) (site-yrs (cdar sitelist)) (measurement-yrs (cdr (assoc site-id measurements))) (mean-yrs (chron-build site-yrs measurement-yrs))) (site-mean (cdr sitelist) (cons (cons site-id mean-yrs) chronologies))))))) (site-mean totals '()))))
Insert placeholders for missing years. The association list <chron-yrs> represents part of a mean tracheidogram chronology, each entry pairing dates with the mean cell file lengths and lists of mean values; the association list <ex-yrs> is the stack of already processed results. If the next chronology entry does not have the date immediately following that of the entry at the top of the stack, we insert placeholders for the missing years, with zero lengths and null lists of mean values.
(define pad-years (lambda (chron-yrs ex-yrs) (cond ((null? chron-yrs) (reverse ex-yrs)) ((or (null? ex-yrs) (= 1 (- (caar chron-yrs) (caar ex-yrs)))) (pad-years (cdr chron-yrs) (cons (car chron-yrs) ex-yrs))) (else (pad-years chron-yrs (cons (list (+ 1 (caar ex-yrs)) 0 '() '()) ex-yrs))))))
Tabulate the mean cell file lengths. Given a list of mean cell file lengths, <todo>, a stack of previously tabulated rows, <tabulated>, and stack of partially completed column values, <buffer>, we push the next length onto the row buffer and update the stacks when the row and table lengths are reached.
(define tabulate-nf (lambda (todo tabulated buffer) (cond ((null? todo) (reverse (if (null? buffer) tabulated (cons (reverse buffer) tabulated)))) ((= (length buffer) 5) (tabulate-nf (cdr todo) (cons (reverse (cons (cadar todo) buffer)) tabulated) '())) (else (tabulate-nf (cdr todo) tabulated (cons (cadar todo) buffer))))))
Tabulate the mean cell dimension values. We build up the table from a list of value lists, one list of values for each year. The <seen> list holds the stack of lists that have already been processed for the current table row, <unseen> those yet to be processed, <table> the stack of already assembled table rows, <row> the stack of current row values, and <valid> a count of the number of valid values encountered so far in this row.
(define tabulate-trach (lambda (seen unseen table row valid) (cond ((null? unseen) (if (= 0 valid) (reverse table) (tabulate-trach '() (reverse seen) (cons (reverse row) table) '() 0))) ((null? (car unseen)) (tabulate-trach (cons '() seen) (cdr unseen) table (cons 0 row) valid)) (else (tabulate-trach (cons (cdar unseen) seen) (cdr unseen) table (cons (caar unseen) row) (+ 1 valid))))))
Tabulate the start year, number of years, and mean values for one site. The association list <chron> holds the mean chronology data, pairing dates with mean tracheidograms represented as lists of mean wall thickness and cell size values. We ignore the wall thicknesses, and only tabulate the cell sizes at the moment.
(define tabulate-mean-chronology (lambda (chron) (let* ((padded-chron (pad-years chron '())) (nf (tabulate-nf padded-chron '() '())) (trach (tabulate-trach '() (map cadddr padded-chron) '() '() 0))) (list (list (caar padded-chron) (length padded-chron)) nf trach))))