diff --git a/corefxlab.sln b/corefxlab.sln
index 1436517046d..d0ad37ae2b5 100644
--- a/corefxlab.sln
+++ b/corefxlab.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.28803.156
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.329
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5E7EB061-B9BC-4DA2-B5E5-859AA7C67695}"
ProjectSection(SolutionItems) = preProject
@@ -110,6 +110,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Numerics.Experimenta
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Numerics.Experimental.Tests", "tests\System.Numerics.Experimental.Tests\System.Numerics.Experimental.Tests.csproj", "{6411FD4E-0CDF-4478-9192-4411DC932314}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.DataFrame", "src\Microsoft.Data\Microsoft.Data.DataFrame.csproj", "{AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.DataFrame.Tests", "tests\Microsoft.Data.Tests\Microsoft.Data.DataFrame.Tests.csproj", "{485FC567-4AEC-4335-B767-283173F64C42}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -696,6 +700,30 @@ Global
{6411FD4E-0CDF-4478-9192-4411DC932314}.Release|x64.Build.0 = Release|Any CPU
{6411FD4E-0CDF-4478-9192-4411DC932314}.Release|x86.ActiveCfg = Release|Any CPU
{6411FD4E-0CDF-4478-9192-4411DC932314}.Release|x86.Build.0 = Release|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Debug|x64.Build.0 = Debug|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Debug|x86.Build.0 = Debug|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Release|x64.ActiveCfg = Release|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Release|x64.Build.0 = Release|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Release|x86.ActiveCfg = Release|Any CPU
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899}.Release|x86.Build.0 = Release|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Debug|x64.Build.0 = Debug|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Debug|x86.Build.0 = Debug|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Release|Any CPU.Build.0 = Release|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Release|x64.ActiveCfg = Release|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Release|x64.Build.0 = Release|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Release|x86.ActiveCfg = Release|Any CPU
+ {485FC567-4AEC-4335-B767-283173F64C42}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -749,6 +777,8 @@ Global
{544D4C8C-B5C6-4C3C-9763-E4CB6AF9A90C} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
{CB424147-4ACB-4C35-AB24-8BD27D6AB1B9} = {4B000021-5278-4F2A-B734-DE49F55D4024}
{6411FD4E-0CDF-4478-9192-4411DC932314} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
+ {AD22AAD4-FCB0-4DB4-BC38-AB6ACD153899} = {4B000021-5278-4F2A-B734-DE49F55D4024}
+ {485FC567-4AEC-4335-B767-283173F64C42} = {3079E458-D0E6-4F99-8CAB-80011D35C7DA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9DD4022C-A010-4A9B-BCC5-171566D4CB17}
diff --git a/src/Microsoft.Data/BaseDataFrameColumn.cs b/src/Microsoft.Data/BaseDataFrameColumn.cs
new file mode 100644
index 00000000000..da1a6d38448
--- /dev/null
+++ b/src/Microsoft.Data/BaseDataFrameColumn.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Data
+{
+ ///
+ /// The base column type. All APIs should have atleast a stub here first
+ ///
+ public abstract class BaseDataFrameColumn
+ {
+ public BaseDataFrameColumn(string name, long length = 0)
+ {
+ Length = length;
+ Name = name;
+ }
+
+ private long _length;
+ public long Length
+ {
+ get => _length;
+ protected set
+ {
+ if (value < 0) throw new ArgumentOutOfRangeException();
+ _length = value;
+ }
+ }
+
+ public long NullCount { get; protected set; }
+
+ public string Name;
+
+ public virtual object this[long rowIndex] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
+
+ public virtual object this[long startIndex, int length] { get { throw new NotImplementedException(); } }
+ }
+}
diff --git a/src/Microsoft.Data/DataFrame.cs b/src/Microsoft.Data/DataFrame.cs
new file mode 100644
index 00000000000..eaadb29314d
--- /dev/null
+++ b/src/Microsoft.Data/DataFrame.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Data
+{
+ ///
+ /// A DataFrame to support indexing, binary operations, sorting, selection and other APIs. This will eventually also expose an IDataView for ML.NET
+ ///
+ public partial class DataFrame
+ {
+ private readonly DataFrameTable _table;
+ public DataFrame()
+ {
+ _table = new DataFrameTable();
+ }
+
+ public long RowCount => _table.RowCount;
+
+ public int ColumnCount => _table.ColumnCount;
+
+ public IList Columns
+ {
+ get
+ {
+ var ret = new List(ColumnCount);
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ ret.Add(_table.Column(i).Name);
+ }
+ return ret;
+ }
+ }
+
+ public BaseDataFrameColumn Column(int index) => _table.Column(index);
+
+ public void InsertColumn(int columnIndex, BaseDataFrameColumn column) => _table.InsertColumn(columnIndex, column);
+
+ public void SetColumn(int columnIndex, BaseDataFrameColumn column) => _table.SetColumn(columnIndex, column);
+
+ public void RemoveColumn(int columnIndex) => _table.RemoveColumn(columnIndex);
+
+ public void RemoveColumn(string columnName) => _table.RemoveColumn(columnName);
+
+ public object this[long rowIndex, int columnIndex]
+ {
+ get => _table.Column(columnIndex)[rowIndex];
+ set => _table.Column(columnIndex)[rowIndex] = value;
+ }
+
+ #region Operators
+ public IList this[long rowIndex]
+ {
+ get
+ {
+ return _table.GetRow(rowIndex);
+ }
+ //TODO?: set?
+ }
+
+ public object this[string columnName]
+ {
+ get
+ {
+ int columnIndex = _table.GetColumnIndex(columnName);
+ if (columnIndex == -1) throw new ArgumentException($"{columnName} does not exist");
+ return _table.Column(columnIndex); //[0, (int)Math.Min(_table.NumRows, Int32.MaxValue)];
+ }
+ }
+
+ public IList> Head(int numberOfRows)
+ {
+ var ret = new List>();
+ for (int i= 0; i< numberOfRows; i++)
+ {
+ ret.Add(this[i]);
+ }
+ return ret;
+ }
+
+ public IList> Tail(int numberOfRows)
+ {
+ var ret = new List>();
+ for (long i = RowCount - numberOfRows; i < RowCount; i++)
+ {
+ ret.Add(this[i]);
+ }
+ return ret;
+ }
+ // TODO: Add strongly typed versions of these APIs
+ #endregion
+ }
+}
diff --git a/src/Microsoft.Data/DataFrameBinaryOperations.cs b/src/Microsoft.Data/DataFrameBinaryOperations.cs
new file mode 100644
index 00000000000..f8f3a9943fb
--- /dev/null
+++ b/src/Microsoft.Data/DataFrameBinaryOperations.cs
@@ -0,0 +1,584 @@
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Data
+{
+ public partial class DataFrame
+ {
+ #region Binary Operations
+
+ public DataFrame Add(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Add(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Add(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Add(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Subtract(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Subtract(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Subtract(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Subtract(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Multiply(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Multiply(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Multiply(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Multiply(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Divide(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Divide(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Divide(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Divide(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Modulo(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Modulo(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Modulo(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Modulo(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame And(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.And(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame And(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.And(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Or(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Or(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Or(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Or(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Xor(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Xor(values[i]);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Xor(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.Xor(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame LeftShift(int value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.LeftShift(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame RightShift(int value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ var newColumn = column.Clone();
+ newColumn._columnContainer.RightShift(value);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Equals(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.Equals(values[i], newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame Equals(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.Equals(value, newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame NotEquals(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.NotEquals(values[i], newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame NotEquals(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.NotEquals(value, newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame GreaterThanOrEqual(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.GreaterThanOrEqual(values[i], newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame GreaterThanOrEqual(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.GreaterThanOrEqual(value, newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame LessThanOrEqual(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.LessThanOrEqual(values[i], newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame LessThanOrEqual(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.LessThanOrEqual(value, newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame GreaterThan(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.GreaterThan(values[i], newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame GreaterThan(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.GreaterThan(value, newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame LessThan(IReadOnlyList values)
+ where T : struct
+ {
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.LessThan(values[i], newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+ public DataFrame LessThan(T value)
+ where T : struct
+ {
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+ column._columnContainer.LessThan(value, newColumn._columnContainer);
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.Data/DataFrameBinaryOperations.tt b/src/Microsoft.Data/DataFrameBinaryOperations.tt
new file mode 100644
index 00000000000..64244e12cfc
--- /dev/null
+++ b/src/Microsoft.Data/DataFrameBinaryOperations.tt
@@ -0,0 +1,68 @@
+<#@ template debug="false" hostspecific="false" language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Text" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ output extension=".cs" #>
+<#@ include file="DataFrameColumnArithmeticTemplate.ttinclude" #>
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Data
+{
+ public partial class DataFrame
+ {
+ #region Binary Operations
+
+<# foreach (MethodConfiguration method in methodConfiguration) { #>
+<# if (method.MethodType == MethodType.BinaryScalar || method.MethodType == MethodType.ComparisonScalar) {#>
+ public DataFrame <#=method.MethodName#>(T value)
+<# } else if (method.MethodType == MethodType.BinaryInt) { #>
+ public DataFrame <#=method.MethodName#>(int value)
+<# } else { #>
+ public DataFrame <#=method.MethodName#>(IReadOnlyList values)
+<# } #>
+ where T : struct
+ {
+<# if (method.MethodType == MethodType.BinaryScalar || method.MethodType == MethodType.ComparisonScalar || method.MethodType == MethodType.BinaryInt) {#>
+<# } else { #>
+ if (values.Count != ColumnCount)
+ {
+ throw new ArgumentException($"values.Count {values.Count} must match the number of columns in the table", nameof(values));
+ }
+<# } #>
+ var newDataFrame = new DataFrame();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ PrimitiveDataFrameColumn column = _table.Column(i) as PrimitiveDataFrameColumn;
+ if (column != null)
+ {
+<# if (method.MethodType == MethodType.ComparisonScalar || method.MethodType == MethodType.Comparison) { #>
+ PrimitiveDataFrameColumn newColumn = column.CreateBoolColumnForCompareOps();
+<# if (method.MethodType == MethodType.ComparisonScalar) { #>
+ column._columnContainer.<#=method.MethodName#>(value, newColumn._columnContainer);
+<# } else { #>
+ column._columnContainer.<#=method.MethodName#>(values[i], newColumn._columnContainer);
+<# } #>
+<# } else if (method.MethodType == MethodType.BinaryScalar || method.MethodType == MethodType.BinaryInt) { #>
+ var newColumn = column.Clone();
+ newColumn._columnContainer.<#=method.MethodName#>(value);
+<# } else { #>
+ var newColumn = column.Clone();
+ newColumn._columnContainer.<#=method.MethodName#>(values[i]);
+<# } #>
+ newDataFrame.InsertColumn(i, newColumn);
+ }
+ }
+ return newDataFrame;
+ }
+
+<# } #>
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.Data/DataFrameBuffer.cs b/src/Microsoft.Data/DataFrameBuffer.cs
new file mode 100644
index 00000000000..ac8aaac7558
--- /dev/null
+++ b/src/Microsoft.Data/DataFrameBuffer.cs
@@ -0,0 +1,116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Microsoft.Data
+{
+ ///
+ /// A basic store to hold values in a DataFrame column. Supports wrapping with an ArrowBuffer
+ ///
+ ///
+ public class DataFrameBuffer
+ where T : struct
+ {
+ // TODO: Change this to Memory
+ public Memory Memory { get; private set; }
+
+ private readonly int _size;
+
+ private int Capacity => Memory.Length / _size;
+
+ public int MaxCapacity => Int32.MaxValue / _size;
+
+ public Span Span
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => MemoryMarshal.Cast(Memory.Span);
+ }
+
+ public int Length { get; internal set; }
+
+ public DataFrameBuffer(int numberOfValues = 8)
+ {
+ _size = Unsafe.SizeOf();
+ if ((long)numberOfValues * _size > MaxCapacity)
+ {
+ throw new ArgumentException($"{numberOfValues} exceeds buffer capacity", nameof(numberOfValues));
+ }
+ Memory = new byte[numberOfValues * _size];
+ }
+
+ public void Append(T value)
+ {
+ if (Length == MaxCapacity)
+ {
+ throw new ArgumentException("Current buffer is full", nameof(value));
+ }
+ EnsureCapacity(1);
+ Span[Length] = value;
+ if (Length < MaxCapacity) ++Length;
+ }
+
+ // TODO: Implement Append(Range of values)?
+ public void EnsureCapacity(int numberOfValues)
+ {
+ long newLength = Length + (long)numberOfValues;
+ if (newLength > MaxCapacity)
+ {
+ throw new ArgumentException("Current buffer is full", nameof(numberOfValues));
+ }
+
+ if (newLength > Capacity)
+ {
+ var newCapacity = Math.Max(newLength * _size, Memory.Length * 2);
+ var memory = new Memory(new byte[newCapacity]);
+ Memory.CopyTo(memory);
+ Memory = memory;
+ }
+ }
+
+ internal T this[int index]
+ {
+ get
+ {
+ if (index > Length) throw new ArgumentOutOfRangeException(nameof(index));
+ return Span[index];
+ }
+ set
+ {
+ if (index > Length) throw new ArgumentOutOfRangeException(nameof(index));
+ Span[index] = value;
+ }
+ }
+
+ internal bool this[int startIndex, int length, IList returnList]
+ {
+ get
+ {
+ if (startIndex > Length) throw new ArgumentOutOfRangeException(nameof(startIndex));
+ long endIndex = Math.Min(Length, startIndex + length);
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ returnList.Add(Span[i]);
+ }
+ return true;
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ Span span = Span;
+ for (int i = 0; i < Length; i++)
+ {
+ sb.Append(span[i]).Append(" ");
+ }
+ return sb.ToString();
+ }
+
+ }
+}
diff --git a/src/Microsoft.Data/DataFrameColumnArithmeticTemplate.cs b/src/Microsoft.Data/DataFrameColumnArithmeticTemplate.cs
new file mode 100644
index 00000000000..5f282702bb0
--- /dev/null
+++ b/src/Microsoft.Data/DataFrameColumnArithmeticTemplate.cs
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/Microsoft.Data/DataFrameColumnArithmeticTemplate.ttinclude b/src/Microsoft.Data/DataFrameColumnArithmeticTemplate.ttinclude
new file mode 100644
index 00000000000..00008039103
--- /dev/null
+++ b/src/Microsoft.Data/DataFrameColumnArithmeticTemplate.ttinclude
@@ -0,0 +1,227 @@
+<#@ template debug="false" hostspecific="false" language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Text" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#+
+ public class TypeConfiguration
+ {
+ public TypeConfiguration(string typeName, string classPrefix = null, string oneLiteral = "1", string zeroLiteral = "0", bool supportsNumeric = true, bool supportsBitwise = true, IEnumerable unsupportedMethods = null)
+ {
+ TypeName = typeName;
+ ClassPrefix = classPrefix ?? char.ToUpper(typeName[0]) + typeName.Substring(1);
+ OneLiteral = oneLiteral;
+ ZeroLiteral = zeroLiteral;
+ SupportsNumeric = supportsNumeric;
+ SupportsBitwise = supportsBitwise;
+ UnsupportedMethods = new HashSet(unsupportedMethods ?? Enumerable.Empty());
+ }
+
+ public string TypeName { get; }
+ public string ClassPrefix { get; }
+ public string OneLiteral { get; }
+ public string ZeroLiteral { get; }
+
+ public bool SupportsNumeric { get; }
+ public bool SupportsBitwise { get; }
+ public ISet UnsupportedMethods { get; }
+ }
+
+ public string GenerateIfStatementHeader(TypeConfiguration type)
+ {
+ string keyword = (type == typeConfiguration[0]) ? "if" : "else if";
+ return $"{keyword} (typeof(T) == typeof({type.TypeName}))";
+ }
+
+ public TypeConfiguration[] typeConfiguration = new []
+ {
+ new TypeConfiguration("bool", oneLiteral:"true", zeroLiteral:"false", supportsNumeric: false, unsupportedMethods: new[] {"LeftShift", "RightShift"}),
+ new TypeConfiguration("byte"),
+ new TypeConfiguration("char", oneLiteral:"(char)1", zeroLiteral:"(char)0"),
+ new TypeConfiguration("decimal", supportsBitwise: false),
+ new TypeConfiguration("double", oneLiteral:"1.0", supportsBitwise: false),
+ new TypeConfiguration("float", oneLiteral:"1.0f", supportsBitwise: false),
+ new TypeConfiguration("int"),
+ new TypeConfiguration("long"),
+ new TypeConfiguration("sbyte", classPrefix:"SByte"),
+ new TypeConfiguration("short"),
+ new TypeConfiguration("uint", classPrefix:"UInt", unsupportedMethods: new[] {"UnaryMinus"}),
+ new TypeConfiguration("ulong", classPrefix:"ULong", unsupportedMethods: new[] {"UnaryMinus"}),
+ new TypeConfiguration("ushort", classPrefix:"UShort", unsupportedMethods: new[] {"UnaryMinus"})
+ };
+
+ public enum MethodType
+ {
+ Unary,
+ UnaryInPlace,
+ BinaryScalar,
+ BinaryInt,
+ Binary,
+ Comparison,
+ ComparisonScalar,
+ Contraction
+ }
+
+ public MethodConfiguration[] methodConfiguration = new []
+ {
+ new MethodConfiguration("Add", MethodType.Binary, "+", isNumeric:true),
+ new MethodConfiguration("Add", MethodType.BinaryScalar, "+", isNumeric:true),
+ new MethodConfiguration("Subtract", MethodType.Binary, "-", isNumeric:true),
+ new MethodConfiguration("Subtract", MethodType.BinaryScalar, "-", isNumeric:true),
+ new MethodConfiguration("Multiply", MethodType.Binary, "*", isNumeric:true), // element-wise product, not matrix product
+ new MethodConfiguration("Multiply", MethodType.BinaryScalar, "*", isNumeric:true),
+ new MethodConfiguration("Divide", MethodType.Binary, "/", isNumeric:true),
+ new MethodConfiguration("Divide", MethodType.BinaryScalar, "/", isNumeric:true),
+ new MethodConfiguration("Modulo", MethodType.Binary, "%", isNumeric:true),
+ new MethodConfiguration("Modulo", MethodType.BinaryScalar, "%", isNumeric:true),
+ new MethodConfiguration("And", MethodType.Binary, "&", isBitwise: true),
+ new MethodConfiguration("And", MethodType.BinaryScalar, "&", isBitwise: true),
+ new MethodConfiguration("Or", MethodType.Binary, "|", isBitwise: true),
+ new MethodConfiguration("Or", MethodType.BinaryScalar, "|", isBitwise: true),
+ new MethodConfiguration("Xor", MethodType.Binary, "^", isBitwise: true),
+ new MethodConfiguration("Xor", MethodType.BinaryScalar, "^", isBitwise: true),
+ new MethodConfiguration("LeftShift", MethodType.BinaryInt, "<<", isBitwise: true),
+ new MethodConfiguration("RightShift", MethodType.BinaryInt, ">>", isBitwise: true),
+
+ new MethodConfiguration("Equals", MethodType.Comparison, "=="),
+ new MethodConfiguration("Equals", MethodType.ComparisonScalar, "=="),
+ new MethodConfiguration("NotEquals", MethodType.Comparison, "!="),
+ new MethodConfiguration("NotEquals", MethodType.ComparisonScalar, "!="),
+ new MethodConfiguration("GreaterThanOrEqual", MethodType.Comparison, ">=", isNumeric:true),
+ new MethodConfiguration("GreaterThanOrEqual", MethodType.ComparisonScalar, ">=", isNumeric:true),
+ new MethodConfiguration("LessThanOrEqual", MethodType.Comparison, "<=", isNumeric:true),
+ new MethodConfiguration("LessThanOrEqual", MethodType.ComparisonScalar, "<=", isNumeric:true),
+ new MethodConfiguration("GreaterThan", MethodType.Comparison, ">", isNumeric:true),
+ new MethodConfiguration("GreaterThan", MethodType.ComparisonScalar, ">", isNumeric:true),
+ new MethodConfiguration("LessThan", MethodType.Comparison, "<", isNumeric:true),
+ new MethodConfiguration("LessThan", MethodType.ComparisonScalar, "<", isNumeric:true),
+ };
+
+ public class MethodConfiguration
+ {
+ public MethodConfiguration(string methodName, MethodType methodType, string op = null, bool isNumeric = false, bool isBitwise = false)
+ {
+ MethodName = methodName;
+ MethodType = methodType;
+ Operator = op;
+ IsNumeric = isNumeric;
+ IsBitwise = isBitwise;
+ }
+
+ public string ResultName => "result";
+
+ public string Op1Name
+ {
+ get
+ {
+ switch (MethodType)
+ {
+ case MethodType.Unary:
+ case MethodType.UnaryInPlace:
+ case MethodType.BinaryScalar:
+ case MethodType.BinaryInt:
+ case MethodType.ComparisonScalar:
+ return "column";
+ case MethodType.Binary:
+ case MethodType.Comparison:
+ case MethodType.Contraction:
+ return "left";
+ default:
+ throw new ArgumentException();
+ };
+ }
+ }
+
+ public string Op2Name
+ {
+ get
+ {
+ switch (MethodType)
+ {
+ case MethodType.BinaryScalar:
+ case MethodType.ComparisonScalar:
+ return "scalar";
+ case MethodType.BinaryInt:
+ return "value";
+ case MethodType.Binary:
+ case MethodType.Comparison:
+ case MethodType.Contraction:
+ return "right";
+ case MethodType.Unary:
+ case MethodType.UnaryInPlace:
+ default:
+ throw new ArgumentException();
+ };
+ }
+ }
+
+ public string MethodName { get; }
+ public MethodType MethodType { get; }
+ public string Operator { get; }
+
+ public string GetMethodSignature(string columnType, string genericType)
+ {
+ var arguments = GetMethodArguments(columnType, genericType);
+ return $"void {MethodName}({arguments})";
+ }
+
+ public string GetSingleArgumentMethodSignature(string columnType, string genericType)
+ {
+ var arguments = GetSingleParameterMethodArguments(columnType, genericType);
+ return $"PrimitiveDataFrameColumnContainer {MethodName}({arguments})";
+ }
+
+ public string GetMethodArguments(string dataFrameType, string genericType)
+ {
+ switch (MethodType)
+ {
+ case MethodType.Unary:
+ case MethodType.UnaryInPlace:
+ return $"{dataFrameType}<{genericType}> {Op1Name}";
+ case MethodType.BinaryScalar:
+ return $"{dataFrameType}<{genericType}> {Op1Name}, {genericType} {Op2Name}";
+ case MethodType.ComparisonScalar:
+ return $"{dataFrameType}<{genericType}> {Op1Name}, {genericType} {Op2Name}, {dataFrameType} ret";
+ case MethodType.BinaryInt:
+ return $"{dataFrameType}<{genericType}> {Op1Name}, int {Op2Name}";
+ case MethodType.Binary:
+ return $"{dataFrameType}<{genericType}> {Op1Name}, {dataFrameType}<{genericType}> {Op2Name}";
+ case MethodType.Comparison:
+ return $"{dataFrameType}<{genericType}> {Op1Name}, {dataFrameType}<{genericType}> {Op2Name}, {dataFrameType} ret";
+ case MethodType.Contraction:
+ return $"{dataFrameType}<{genericType}> {Op1Name}, {dataFrameType}<{genericType}> {Op2Name}, int[] leftAxes, int[] rightAxes";
+ default:
+ throw new ArgumentException();
+ }
+ }
+
+ public string GetSingleParameterMethodArguments(string dataFrameType, string genericType)
+ {
+ switch (MethodType)
+ {
+ case MethodType.Unary:
+ case MethodType.UnaryInPlace:
+ throw new ArgumentException();
+ return $"{dataFrameType}<{genericType}> {Op1Name}";
+ case MethodType.BinaryScalar:
+ return $"{genericType} {Op2Name}";
+ case MethodType.ComparisonScalar:
+ return $"{genericType} {Op2Name}, {dataFrameType} ret";
+ case MethodType.BinaryInt:
+ return $"int {Op2Name}";
+ case MethodType.Binary:
+ return $"{dataFrameType}<{genericType}> {Op2Name}";
+ case MethodType.Comparison:
+ return $"{dataFrameType}<{genericType}> {Op2Name}, {dataFrameType} ret";
+ case MethodType.Contraction:
+ throw new ArgumentException();
+ return $"{dataFrameType}<{genericType}> {Op1Name}, {dataFrameType}<{genericType}> {Op2Name}, int[] leftAxes, int[] rightAxes";
+ default:
+ throw new ArgumentException();
+ }
+ }
+
+ public bool IsNumeric { get; }
+ public bool IsBitwise { get; }
+ }
+#>
diff --git a/src/Microsoft.Data/DataFrameTable.cs b/src/Microsoft.Data/DataFrameTable.cs
new file mode 100644
index 00000000000..6eebbbf1628
--- /dev/null
+++ b/src/Microsoft.Data/DataFrameTable.cs
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Data
+{
+ ///
+ /// A DataFrameTable is just a container that holds a number of DataFrameColumns. It mainly acts as a convenient store to allow DataFrame to implement its algorithms
+ ///
+ internal class DataFrameTable
+ {
+ private IList _columns;
+
+ private List _columnNames = new List();
+
+ private Dictionary _columnNameToIndexDictionary = new Dictionary(StringComparer.Ordinal);
+
+ public long RowCount { get; private set; }
+
+ public int ColumnCount { get; private set; }
+
+ public DataFrameTable()
+ {
+ _columns = new List();
+ }
+
+ private DataFrameTable(IList columns)
+ {
+ columns = columns ?? throw new ArgumentNullException(nameof(columns));
+ _columns = columns;
+ ColumnCount = columns.Count;
+ if (columns.Count > 0)
+ {
+ RowCount = columns[0].Length;
+ for (var i = 0; i < columns.Count; i++)
+ {
+ _columnNames.Add(columns[i].Name);
+ _columnNameToIndexDictionary.Add(columns[i].Name, i);
+ }
+ }
+ }
+
+ public DataFrameTable(BaseDataFrameColumn column) : this(new List { column }) { }
+
+ public BaseDataFrameColumn Column(int columnIndex) => _columns[columnIndex];
+
+ public IList GetRow(long rowIndex)
+ {
+ var ret = new List();
+ for (int i = 0; i < ColumnCount; i++)
+ {
+ ret.Add(Column(i)[rowIndex]);
+ }
+ return ret;
+ }
+
+ public void InsertColumn(int columnIndex, IEnumerable column, string columnName)
+ where T : struct
+ {
+ column = column ?? throw new ArgumentNullException(nameof(column));
+ if ((uint)columnIndex > _columns.Count)
+ {
+ throw new ArgumentException($"Invalid columnIndex {columnIndex} passed into Table.AddColumn");
+ }
+ BaseDataFrameColumn newColumn = new PrimitiveDataFrameColumn(columnName, column);
+ InsertColumn(columnIndex, newColumn);
+ }
+
+ public void InsertColumn(int columnIndex, BaseDataFrameColumn column)
+ {
+ column = column ?? throw new ArgumentNullException(nameof(column));
+ if ((uint)columnIndex > _columns.Count)
+ {
+ throw new ArgumentException($"Invalid columnIndex {columnIndex} passed into Table.AddColumn");
+ }
+ if (RowCount > 0 && column.Length != RowCount)
+ {
+ throw new ArgumentException($"Column's length {column.Length} must match Table's length {RowCount}");
+ }
+ if (_columnNameToIndexDictionary.ContainsKey(column.Name))
+ {
+ throw new ArgumentException($"Table already contains a column called {column.Name}");
+ }
+ RowCount = column.Length;
+ _columnNames.Insert(columnIndex, column.Name);
+ _columnNameToIndexDictionary[column.Name] = columnIndex;
+ _columns.Insert(columnIndex, column);
+ ColumnCount++;
+ }
+
+ public void SetColumn(int columnIndex, BaseDataFrameColumn column)
+ {
+ column = column ?? throw new ArgumentNullException(nameof(column));
+ if ((uint)columnIndex >= ColumnCount)
+ {
+ throw new ArgumentException($"Invalid columnIndex {columnIndex} passed in to Table.SetColumn");
+ }
+ if (RowCount > 0 && column.Length != RowCount)
+ {
+ throw new ArgumentException($"Column's length {column.Length} must match table's length {RowCount}");
+ }
+ if (_columnNameToIndexDictionary.ContainsKey(column.Name))
+ {
+ throw new ArgumentException($"Table already contains a column called {column.Name}");
+ }
+ _columnNameToIndexDictionary.Remove(_columnNames[columnIndex]);
+ _columnNames[columnIndex] = column.Name;
+ _columnNameToIndexDictionary[column.Name] = columnIndex;
+ _columns[columnIndex] = column;
+ }
+
+ public void RemoveColumn(int columnIndex)
+ {
+ _columnNameToIndexDictionary.Remove(_columnNames[columnIndex]);
+ _columnNames.RemoveAt(columnIndex);
+ _columns.RemoveAt(columnIndex);
+ ColumnCount--;
+ }
+
+ public void RemoveColumn(string columnName)
+ {
+ int columnIndex = GetColumnIndex(columnName);
+ if (columnIndex != -1)
+ {
+ RemoveColumn(columnIndex);
+ }
+ }
+
+ public int GetColumnIndex(string columnName)
+ {
+ if (_columnNameToIndexDictionary.TryGetValue(columnName, out int columnIndex))
+ {
+ return columnIndex;
+ }
+ return -1;
+ }
+
+ }
+}
diff --git a/src/Microsoft.Data/Microsoft.Data.DataFrame.csproj b/src/Microsoft.Data/Microsoft.Data.DataFrame.csproj
new file mode 100644
index 00000000000..66fedd2b7a7
--- /dev/null
+++ b/src/Microsoft.Data/Microsoft.Data.DataFrame.csproj
@@ -0,0 +1,114 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+ TextTemplatingFileGenerator
+ BaseColumn.BinaryOperations.cs
+
+
+ TextTemplatingFileGenerator
+ BaseColumn.BinaryOperators.cs
+
+
+ TextTemplatingFileGenerator
+ DataFrameBinaryOperations.cs
+
+
+ TextTemplatingFileGenerator
+ DataFrameBinaryOperators.cs
+
+
+ TextTemplatingFileGenerator
+ DataFrameColumnArithmeticTemplate.cs
+
+
+ TextTemplatingFileGenerator
+ PrimitiveColumn.BinaryOperations.cs
+
+
+ TextTemplatingFileGenerator
+ PrimitiveColumnArithmetic.cs
+
+
+ TextTemplatingFileGenerator
+ PrimitiveColumnContainer.BinaryOperations.cs
+
+
+ TextTemplatingFileGenerator
+ PrimitiveDataFrameColumnArithmetic.cs
+
+
+ TextTemplatingFileGenerator
+ PrimitiveDataFrameColumnContainer.BinaryOperations.cs
+
+
+
+
+
+
+
+
+
+ True
+ True
+ BaseColumn.BinaryOperations.tt
+
+
+ True
+ True
+ BaseColumn.BinaryOperators.tt
+
+
+ True
+ True
+ DataFrameBinaryOperations.tt
+
+
+ True
+ True
+ DataFrameBinaryOperators.tt
+
+
+ True
+ True
+ DataFrameColumnArithmeticTemplate.ttinclude
+
+
+ True
+ True
+ PrimitiveColumn.BinaryOperations.tt
+
+
+ True
+ True
+ PrimitiveColumnArithmetic.tt
+
+
+ True
+ True
+ PrimitiveColumnContainer.BinaryOperations.tt
+
+
+ True
+ True
+ PrimitiveDataFrameColumnArithmetic.tt
+
+
+ True
+ True
+ PrimitiveDataFrameColumnContainer.BinaryOperations.tt
+
+
+
+
diff --git a/src/Microsoft.Data/PrimitiveDataFrameColumn.cs b/src/Microsoft.Data/PrimitiveDataFrameColumn.cs
new file mode 100644
index 00000000000..ec0798aec5f
--- /dev/null
+++ b/src/Microsoft.Data/PrimitiveDataFrameColumn.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace Microsoft.Data
+{
+ ///
+ /// A column to hold primitive values such as int, float etc. Other value types are not really supported
+ ///
+ ///
+ public class PrimitiveDataFrameColumn : BaseDataFrameColumn
+ where T : struct
+ {
+ internal PrimitiveDataFrameColumnContainer _columnContainer;
+
+ public Type DataType = typeof(T);
+
+ internal PrimitiveDataFrameColumn(string name, PrimitiveDataFrameColumnContainer column) : base(name, column.Length)
+ {
+ _columnContainer = column;
+ }
+
+ public PrimitiveDataFrameColumn(string name, IEnumerable values) : base(name)
+ {
+ _columnContainer = new PrimitiveDataFrameColumnContainer(values);
+ Length = _columnContainer.Length;
+ }
+
+ public PrimitiveDataFrameColumn(string name, bool isNullable = true) : base(name)
+ {
+ _columnContainer = new PrimitiveDataFrameColumnContainer();
+ }
+
+ public override object this[long startIndex, int length] {
+ get
+ {
+ if (startIndex > Length )
+ {
+ throw new ArgumentException($"Indexer arguments exceed Length {Length} of the Column");
+ }
+ return _columnContainer[startIndex, length];
+ }
+ }
+
+ // This method involves boxing
+ public override object this[long rowIndex]
+ {
+ get
+ {
+ return _columnContainer[rowIndex];
+ }
+ set
+ {
+ _columnContainer[rowIndex] = (T)value;
+ }
+ }
+
+ public void Add(T value) => _columnContainer.Add(value);
+
+ public override string ToString()
+ {
+ return $"{Name}: {_columnContainer.ToString()}";
+ }
+
+ public PrimitiveDataFrameColumn Clone()
+ {
+ PrimitiveDataFrameColumnContainer newColumnContainer = _columnContainer.Clone();
+ return new PrimitiveDataFrameColumn(Name, newColumnContainer);
+ }
+
+ internal PrimitiveDataFrameColumn CreateBoolColumnForCompareOps()
+ {
+ PrimitiveDataFrameColumnContainer newColumnContainer = _columnContainer.CreateBoolContainerForCompareOps();
+ return new PrimitiveDataFrameColumn(Name, newColumnContainer);
+ }
+
+ }
+}
diff --git a/src/Microsoft.Data/PrimitiveDataFrameColumnArithmetic.cs b/src/Microsoft.Data/PrimitiveDataFrameColumnArithmetic.cs
new file mode 100644
index 00000000000..28b66b26315
--- /dev/null
+++ b/src/Microsoft.Data/PrimitiveDataFrameColumnArithmetic.cs
@@ -0,0 +1,4138 @@
+
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.Data
+{
+ internal interface IPrimitiveDataFrameColumnArithmetic
+ where T : struct
+ {
+ void Add(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void Add(PrimitiveDataFrameColumnContainer column, T scalar);
+ void Subtract(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void Subtract(PrimitiveDataFrameColumnContainer column, T scalar);
+ void Multiply(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void Multiply(PrimitiveDataFrameColumnContainer column, T scalar);
+ void Divide(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void Divide(PrimitiveDataFrameColumnContainer column, T scalar);
+ void Modulo(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void Modulo(PrimitiveDataFrameColumnContainer column, T scalar);
+ void And(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void And(PrimitiveDataFrameColumnContainer column, T scalar);
+ void Or(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void Or(PrimitiveDataFrameColumnContainer column, T scalar);
+ void Xor(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right);
+ void Xor(PrimitiveDataFrameColumnContainer column, T scalar);
+ void LeftShift(PrimitiveDataFrameColumnContainer column, int value);
+ void RightShift(PrimitiveDataFrameColumnContainer column, int value);
+ void Equals(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right, PrimitiveDataFrameColumnContainer ret);
+ void Equals(PrimitiveDataFrameColumnContainer column, T scalar, PrimitiveDataFrameColumnContainer ret);
+ void NotEquals(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right, PrimitiveDataFrameColumnContainer ret);
+ void NotEquals(PrimitiveDataFrameColumnContainer column, T scalar, PrimitiveDataFrameColumnContainer ret);
+ void GreaterThanOrEqual(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right, PrimitiveDataFrameColumnContainer ret);
+ void GreaterThanOrEqual(PrimitiveDataFrameColumnContainer column, T scalar, PrimitiveDataFrameColumnContainer ret);
+ void LessThanOrEqual(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right, PrimitiveDataFrameColumnContainer ret);
+ void LessThanOrEqual(PrimitiveDataFrameColumnContainer column, T scalar, PrimitiveDataFrameColumnContainer ret);
+ void GreaterThan(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right, PrimitiveDataFrameColumnContainer ret);
+ void GreaterThan(PrimitiveDataFrameColumnContainer column, T scalar, PrimitiveDataFrameColumnContainer ret);
+ void LessThan(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right, PrimitiveDataFrameColumnContainer ret);
+ void LessThan(PrimitiveDataFrameColumnContainer column, T scalar, PrimitiveDataFrameColumnContainer ret);
+ }
+
+ internal static class PrimitiveDataFrameColumnArithmetic
+ where T : struct
+ {
+ public static IPrimitiveDataFrameColumnArithmetic Instance => PrimitiveDataFrameColumnArithmetic.GetArithmetic();
+ }
+
+ internal static class PrimitiveDataFrameColumnArithmetic
+ {
+ public static IPrimitiveDataFrameColumnArithmetic GetArithmetic()
+ where T : struct
+ {
+ if (typeof(T) == typeof(bool))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new BoolArithmetic();
+ }
+ else if (typeof(T) == typeof(byte))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new ByteArithmetic();
+ }
+ else if (typeof(T) == typeof(char))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new CharArithmetic();
+ }
+ else if (typeof(T) == typeof(decimal))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new DecimalArithmetic();
+ }
+ else if (typeof(T) == typeof(double))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new DoubleArithmetic();
+ }
+ else if (typeof(T) == typeof(float))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new FloatArithmetic();
+ }
+ else if (typeof(T) == typeof(int))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new IntArithmetic();
+ }
+ else if (typeof(T) == typeof(long))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new LongArithmetic();
+ }
+ else if (typeof(T) == typeof(sbyte))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new SByteArithmetic();
+ }
+ else if (typeof(T) == typeof(short))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new ShortArithmetic();
+ }
+ else if (typeof(T) == typeof(uint))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new UIntArithmetic();
+ }
+ else if (typeof(T) == typeof(ulong))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new ULongArithmetic();
+ }
+ else if (typeof(T) == typeof(ushort))
+ {
+ return (IPrimitiveDataFrameColumnArithmetic)new UShortArithmetic();
+ }
+ throw new NotSupportedException();
+ }
+ }
+
+ internal class BoolArithmetic : IPrimitiveDataFrameColumnArithmetic
+ {
+ public void Add(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right)
+ {
+ throw new NotSupportedException();
+ }
+ public void Add(PrimitiveDataFrameColumnContainer column, bool scalar)
+ {
+ throw new NotSupportedException();
+ }
+ public void Subtract(PrimitiveDataFrameColumnContainer left, PrimitiveDataFrameColumnContainer right)
+ {
+ throw new NotSupportedException();
+ }
+ public void Subtract(PrimitiveDataFrameColumnContainer