diff --git a/datarecord/datarecord.go b/datarecord/datarecord.go index 11dde98..daf84e1 100644 --- a/datarecord/datarecord.go +++ b/datarecord/datarecord.go @@ -11,17 +11,36 @@ import ( "time" ) -type void struct{} +type ColumnStatistic struct { + Name string + Minimum float32 + Maximum float32 + Average float32 +} + type dataReaderData map[time.Time]map[string][]float32 + +type columnStatistic struct { + minimum float32 + maximum float32 + sum float32 + count int +} + +type dataColumns struct { + names []string + statistic map[string][]columnStatistic +} + type dataReader struct { dateFormat string dateColumn int pivotColumn int delimiter []byte - columnNames map[string]void - points int - data dataReaderData + columns dataColumns + points int + data dataReaderData } type dataRecord struct { @@ -41,7 +60,7 @@ var checkErr = func(err error) { func GetDataReader() (reader dataReader) { - reader.columnNames = make(map[string]void) + reader.columns.initialize() reader.data = make(dataReaderData) return @@ -65,14 +84,12 @@ func (obj *dataReader) WithPivotColumn(column int) *dataReader { func (obj *dataReader) WithDelimiter(delimiter string) *dataReader { obj.delimiter = []byte(delimiter) return obj -} +} func (obj *dataReader) ReadDataRecord(data string) { record := obj.getDataRecord(data) - if len(record.pivot) != 0 { - obj.columnNames[record.pivot] = void{} - } + obj.columns.addDataRecord(record) if obj.points < len(record.points) { obj.points = len(record.points) } @@ -84,26 +101,15 @@ func (obj *dataReader) ReadDataRecord(data string) { obj.data[record.dateTime][record.pivot] = record.points } -func (obj *dataReader) GetColumns() []string { - - columns := make([]string, 0, 10) - for _, name := range obj.getColumnNames() { - if name == "" { - name = "Column" - } - for i := 1; i < obj.points+1; i++ { - columns = append(columns, fmt.Sprintf("%s %d", name, i)) - } - } - - return columns +func (obj *dataReader) GetColumns() []ColumnStatistic { + return obj.columns.getColumnStatistics() } func (obj *dataReader) GetDataRows() []string { rows := make([]string, 0, 10) - columns := obj.getColumnNames() + columns := obj.columns.getColumnNames() buffer := new(bytes.Buffer) writer := bufio.NewWriter(buffer) @@ -144,12 +150,31 @@ func (obj *dataReader) GetDataRows() []string { /////////////////////////////////////////////////////// -func (obj *dataReader) getColumnNames() []string { +/////////////////////////////////////////////////////// +// dataColumns + +func (obj *dataColumns) initialize() { + obj.statistic = make(map[string][]columnStatistic) +} + +func (obj *dataColumns) addDataRecord(data dataRecord) { + if _, ok := obj.statistic[data.pivot]; !ok { + obj.statistic[data.pivot] = make([]columnStatistic, len(data.points)) + } + + element := obj.statistic[data.pivot] + for i, dataPoint := range data.points { + element[i].addDataPoint(dataPoint) + } + +} + +func (obj *dataColumns) getColumnNames() []string { columns := make([]string, 0, 10) - if len(obj.columnNames) == 0 { + if len(obj.statistic) == 0 { columns = append(columns, "") } else { - for name := range obj.columnNames { + for name := range obj.statistic { columns = append(columns, name) } } @@ -159,6 +184,41 @@ func (obj *dataReader) getColumnNames() []string { return columns } +func (obj *dataColumns) getColumnStatistics() []ColumnStatistic { + columns := make([]ColumnStatistic, 0) + + for name, pivotData := range obj.statistic { + if name == "" { + name = "Column" + } + for i, data := range pivotData { + columns = append(columns, ColumnStatistic{ + Name: fmt.Sprintf("%s %d", name, i+1), + Minimum: data.minimum, + Maximum: data.maximum, + Average: data.sum / float32(data.count), + }) + } + } + + return columns +} + +/////////////////////////////////////////////////////// +// columnStatistic + +func (obj *columnStatistic) addDataPoint(data float32) { + if obj.count == 0 { + obj.minimum = data + obj.maximum = data + } else { + obj.minimum = min(obj.minimum, data) + obj.maximum = max(obj.maximum, data) + } + obj.sum += data + obj.count++ +} + /////////////////////////////////////////////////////// // dateRecord diff --git a/datarecord/datarecord_test.go b/datarecord/datarecord_test.go index b04bd47..e6a25eb 100644 --- a/datarecord/datarecord_test.go +++ b/datarecord/datarecord_test.go @@ -95,11 +95,11 @@ func Test_dataReader_GetColumns(t *testing.T) { want []string }{ { - "test 1", dataReader{columnNames: map[string]void{"first": {}, "second": {}}, points: 3}, + "test 1", dataReader{columns: dataColumns{statistic: map[string][]columnStatistic{"first": {}, "second": {}}}, points: 3}, []string{"first 1", "first 2", "first 3", "second 1", "second 2", "second 3"}, }, { - "test 2", dataReader{columnNames: map[string]void{}, points: 3}, + "test 2", dataReader{columns: dataColumns{}, points: 3}, []string{"Column 1", "Column 2", "Column 3"}, }, } @@ -148,3 +148,67 @@ func Test_dataReader_ReadDataRecord(t *testing.T) { }) } } + +func Test_dataColumns_addDataRecord(t *testing.T) { + tests := []struct { + name string + obj dataColumns + data dataRecord + want dataColumns + }{ + { + "test 1", + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{}}, + dataRecord{time.Date(2012, time.October, 15, 10, 1, 0, 0, time.Local), "first", []float32{1, 2, 3}}, + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{"first": {{1, 1, 1, 1}, {2, 2, 2, 1}, {3, 3, 3, 1}}}}, + }, + { + "test 2", + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{}}, + dataRecord{time.Date(2012, time.October, 15, 10, 1, 0, 0, time.Local), "", []float32{1, 2, 3}}, + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{"": {{1, 1, 1, 1}, {2, 2, 2, 1}, {3, 3, 3, 1}}}}, + }, + { + "test 3", + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{"first": {{1, 1, 1, 1}, {2, 2, 2, 1}, {3, 3, 3, 1}}}}, + dataRecord{time.Date(2012, time.October, 15, 10, 1, 0, 0, time.Local), "first", []float32{10, -2, 3}}, + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{"first": {{1, 10, 11, 2}, {-2, 2, 0, 2}, {3, 3, 6, 2}}}}, + }, + } + for _, tt := range tests { + + t.Run(tt.name, func(t *testing.T) { + //tt.obj.initialize() + tt.obj.addDataRecord(tt.data) + if !reflect.DeepEqual(tt.obj, tt.want) { + t.Errorf("GetDataRecord():\n got %v\n want %v", tt.obj, tt.want) + } + }) + } +} + +func Test_dataColumns_getColumnStatistics(t *testing.T) { + tests := []struct { + name string + obj dataColumns + want []ColumnStatistic + }{ + { + "test 1", + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{"first": {{1, 1, 1, 1}, {2, 2, 2, 1}, {3, 3, 3, 1}}}}, + []ColumnStatistic{{"first 1", 1, 1, 1}, {"first 2", 2, 2, 2}, {"first 3", 3, 3, 3}}, + }, + { + "test 2", + dataColumns{names: []string{}, statistic: map[string][]columnStatistic{"": {{1, 1, 1, 1}, {2, 2, 2, 1}, {3, 3, 3, 1}}}}, + []ColumnStatistic{{"Column 1", 1, 1, 1}, {"Column 2", 2, 2, 2}, {"Column 3", 3, 3, 3}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.obj.getColumnStatistics(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("dataColumns.getColumnStatistics() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/go.mod b/go.mod index a3d56a5..60763bd 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/nikolainp/toGraph -go 1.20 +go 1.21 require github.com/go-test/deep v1.1.0 diff --git a/toGraph.go b/toGraph.go index 766b85d..d5b2864 100644 --- a/toGraph.go +++ b/toGraph.go @@ -20,7 +20,6 @@ var checkErr = func(err error) { } // TODO: column names -// TODO: graph name + output file name // TODO: statistic by columns func main() { @@ -72,7 +71,7 @@ func processFile(sIn io.Reader, sOut io.Writer, config state.Configuration, titl data := struct { Title string - Columns []string + Columns []datarecord.ColumnStatistic DataRows []string }{ Title: title, diff --git a/toGraphTemplate.go b/toGraphTemplate.go index 7855c37..db202bb 100644 --- a/toGraphTemplate.go +++ b/toGraphTemplate.go @@ -42,7 +42,7 @@ const graphTemplate = ` var data = new google.visualization.DataTable(); data.addColumn('date', 'Date'); {{range $column := .Columns -}} - data.addColumn('number', '{{$column}}'); + data.addColumn('number', '{{$column.Name}}'); {{end}} // [new Date(2314, 2, 16), 24045, 12374], @@ -89,10 +89,18 @@ const graphTemplate = `