public static ICube Pivot(this ICube cube, string[] fieldsToAggregateBy, AggregationAction aggregationAction) { //for now, aggregate only works on numerical fields and performs a sum foreach (var fieldToAggregateBy in fieldsToAggregateBy) { if (!cube.DataTypes.ContainsKey(fieldToAggregateBy)) { throw new Exception($"Cannot aggregate on field {fieldToAggregateBy} as it is not present"); } } var types = cube.DataTypes.Keys.ToList(); var ixs = fieldsToAggregateBy.Select(f => types.IndexOf(f)).ToArray(); var rows = cube.GetAllRows(); var distinctValues = rows.Select(x => string.Join("~", ixs.Select(ix => x.MetaData[ix]?.ToString() ?? string.Empty))).Distinct(); var outCube = new ResultCube(); var oT = new Dictionary <string, Type>(); foreach (var fieldToAggregateBy in fieldsToAggregateBy) { oT.Add(fieldToAggregateBy, cube.DataTypes[fieldToAggregateBy]); } outCube.Initialize(oT); var aggData = new Dictionary <string, double>(); var aggDataCount = new Dictionary <string, int>(); var metaDict = new Dictionary <string, object[]>(); foreach (var row in rows) { var rowKey = string.Join("~", ixs.Select(i => row.MetaData[i]?.ToString() ?? string.Empty)); if (!aggData.ContainsKey(rowKey)) { if (aggregationAction == AggregationAction.Min) { aggData[rowKey] = double.MaxValue; } else if (aggregationAction == AggregationAction.Max) { aggData[rowKey] = double.MinValue; } else { aggData[rowKey] = 0; } aggDataCount[rowKey] = 0; var filetedMetaData = new object[ixs.Length]; for (var i = 0; i < ixs.Length; i++) { filetedMetaData[i] = row.MetaData[ixs[i]]; } metaDict[rowKey] = filetedMetaData; } switch (aggregationAction) { case AggregationAction.Sum: aggData[rowKey] += row.Value; break; case AggregationAction.Average: aggData[rowKey] += row.Value; aggDataCount[rowKey]++; break; case AggregationAction.Min: aggData[rowKey] = System.Math.Min(aggData[rowKey], row.Value); break; case AggregationAction.Max: aggData[rowKey] = System.Math.Max(aggData[rowKey], row.Value); break; } } //final post-processing for average foreach (var rowKey in aggData.Keys.ToList()) { var rowDict = new Dictionary <string, object>(); if (aggregationAction == AggregationAction.Average) { aggData[rowKey] /= aggDataCount[rowKey]; } outCube.AddRow(metaDict[rowKey], aggData[rowKey]); } return(outCube); }
public static ICube Merge(this ICube baseCube, ICube otherCube) { //add check that common fields are of them same type... foreach (var kv in baseCube.DataTypes.Where(x => otherCube.DataTypes.Keys.Contains(x.Key))) { if (kv.Value != otherCube.DataTypes[kv.Key]) { throw new Exception($"Data types dont match for field {kv.Key}"); } } var newDataTypes = new Dictionary <string, Type>(); foreach (var kv in baseCube.DataTypes) { newDataTypes[kv.Key] = kv.Value; } foreach (var kv in otherCube.DataTypes) { newDataTypes[kv.Key] = kv.Value; } var o = new ResultCube(); o.Initialize(newDataTypes); var baseRows = baseCube.GetAllRows().ToArray(); var otherRows = otherCube.GetAllRows().ToArray(); var baseIx = baseCube.DataTypes.Keys.Select(k => o.GetColumnIndex(k)).ToArray(); var otherIx = otherCube.DataTypes.Keys.Select(k => o.GetColumnIndex(k)).ToArray(); var cleanRow = o.DataTypes.Select(kv => GetDefaultValue(kv.Value)).ToArray(); for (var i = 0; i < baseRows.Length; i++) { var row = new object[cleanRow.Length]; Array.Copy(cleanRow, row, row.Length); var br = baseRows[i]; for (var j = 0; j < baseIx.Length; j++) { row[baseIx[j]] = br.MetaData[j]; } o.AddRow(row, br.Value); } for (var i = 0; i < otherRows.Length; i++) { var br = otherRows[i]; var row = new object[cleanRow.Length]; Array.Copy(cleanRow, row, row.Length); for (var j = 0; j < baseIx.Length; j++) { row[baseIx[j]] = br.MetaData[j]; } o.AddRow(row, br.Value); } return(o); }