REBOL [ Title: "Condense Data" Date: 18-Feb-2002 Version: 0.0.1 File: %condense.r Author: "Matthew S. Licholai" Rights: "(C)2002 Matthew S. Licholai" Usage: {Examples: do %get-stock.r ^-^-either exists? %IBM.csv [ ^-^-^-stock-data: get-stock/data/retrieve "IBM" (now/date - 50) now/date %IBM.csv ^-^-][ ^-^-^-stock-data: get-stock/data/store "IBM" (now/date - 50) now/date %IBM.csv ^-^-] ^-^-blk: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25] ^-^-blk-r: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25] ^-^-blk2: [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ] ^-^-reverse blk-r ^-^-blk-3: reduce [blk blk blk] ^-^-blk-4: reduce [blk blk2 blk-r blk] ^-^-probe aggregate-10 blk-3 ^-^-probe aggregate-5 blk-4 ^-^-print length? first stock-data ^-^-answer: aggregate-weeks stock-data ^-^-print length? first answer ^-^-dt: first answer ^-^-answer: next answer ^-^-do %q-plot.r ^-^-view quick-plot [ ^-^-^-500x500 ^-^-^-x-data [(dt)] ^-^-^-stock [(answer)] ^-^-^-x-axis 7 border ^-^-^-x-grid 7 ^-^-^-y-axis 5 ^-^-^-y-grid 5 ^-^-] ^-^-} Purpose: {Provide a series of functions for aggregating data. Specifically designed for taking daily stock data and condensing it into weekly or monthly data series, but flexible enough for most data aggregation tasks.} History: [0.0.1 [18-Feb-2002 "Initial version."]] Email: m.s.licholai@ieee.org Category: [math file util 3 2] ] condense: func ["aggregate data by number of items or days" data [block!] "data to condense" test [function!] "return true when value is the last to condense" /test-nr nr-test "vector number to use for testing" /combiner comb-fun [function!] "how to aggregate the values" /local ][ ;see if we have a block of blocks blocks: block! = type? first data data-width: either blocks [length? data][1] any [test-nr nr-test: 1] ; default to testing the first block (if more than one) if not combiner [ ;;; if we don't have a combiner function specified by the user use a default ; of averaging the values in each vector comb-fun: func [temps [block!] /local result sum number ][ result: copy [] foreach col temps [ either all [(not tail? col) (date! = type? first col)][append result last col clear head col][ sum: 0.0 number: 0.0 foreach val col [ sum: sum + val number: number + 1.0 ] clear head col if (number > 0) [append result (sum / number)] ] ] return result ] ] result: copy [] ; where we store the condensed data until done temp-blks: copy [] ; temp vectors while we aggregate ; create result and temp blocks for each data vector loop data-width [append/only result copy [] append/only temp-blks copy []] until [ for i 1 data-width 1 [ col: pick data i val: either blocks [first col][col] append (pick temp-blks i) val ;append val to the temp-data vector ] val: either blocks [first pick data nr-test][pick data nr-test] if test val [ vals: comb-fun temp-blks for i 1 data-width 1 [ append (pick result i) (pick vals i) ] ] either blocks [for i 1 data-width 1 [poke data i (t: next pick data i)]][t: data: next data] tail? t ] ; add the remaining values at the end vals: comb-fun temp-blks for i 1 data-width 1 [ if (val: pick vals i) [append (pick result i) val] ] if blocks [for i 1 data-width 1 [poke data i (head pick data i)]]; restore the head of each data block return either blocks [head result][first result] ] number-vals: func [number][ return func [ t-val /reset /local holder][ holder: [] append holder 1 if reset [holder: clear head holder ] either ((length? holder) >= number ) [ holder: clear head holder return true ][ return false ] ] ] same-week?: func [test-day [date!] /local day last-day result][ last-day: [1] day: test-day/weekday result: (day < first last-day) last-day: append clear last-day day return result ] same-month?: func [test-day [date!] /local day last-day result][ last-day: [1] day: test-day/day result: (day < first last-day) last-day: append clear last-day day return result ] ;; some concrete implementations aggregate-10: func [ data ][ ten-vals: number-vals 10 ten-vals/reset 1 return condense data :ten-vals ] aggregate-5: func [ data ][ five-vals: number-vals 5 five-vals/reset 1 return condense data :five-vals ] aggregate-weeks: func [data /local ][ return condense data :same-week? data ] aggregate-months: func [data /local ][ return condense data :same-month? data ] ;; tests