diff --git a/src/Microsoft.Data.Analysis/IDataView.Extension.cs b/src/Microsoft.Data.Analysis/IDataView.Extension.cs index aae5c38e63..303eaf3b76 100644 --- a/src/Microsoft.Data.Analysis/IDataView.Extension.cs +++ b/src/Microsoft.Data.Analysis/IDataView.Extension.cs @@ -116,6 +116,10 @@ public static DataFrame ToDataFrame(this IDataView dataView, long maxRows, param { dataFrameColumns.Add(new StringDataFrameColumn(dataViewColumn.Name)); } + else if (type is VectorDataViewType vectorType) + { + dataFrameColumns.Add(GetVectorDataFrame(vectorType, dataViewColumn.Name)); + } else { throw new NotSupportedException(String.Format(Microsoft.Data.Strings.NotSupportedColumnType, type.RawType.Name)); @@ -143,6 +147,66 @@ public static DataFrame ToDataFrame(this IDataView dataView, long maxRows, param return new DataFrame(dataFrameColumns); } + + private static DataFrameColumn GetVectorDataFrame(VectorDataViewType vectorType, string name) + { + var itemType = vectorType.ItemType; + + if (itemType.RawType == typeof(bool)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(byte)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(double)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(float)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(int)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(long)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(sbyte)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(short)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(uint)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(ulong)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(ushort)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(char)) + { + return new VBufferDataFrameColumn(name); + } + else if (itemType.RawType == typeof(decimal)) + { + return new VBufferDataFrameColumn(name); + } + + throw new NotSupportedException(String.Format(Microsoft.Data.Strings.VectorSubTypeNotSupported, itemType.ToString())); + } } } diff --git a/src/Microsoft.Data.Analysis/Strings.Designer.cs b/src/Microsoft.Data.Analysis/Strings.Designer.cs index 9cbf90f38e..38cabf7ecb 100644 --- a/src/Microsoft.Data.Analysis/Strings.Designer.cs +++ b/src/Microsoft.Data.Analysis/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.Data { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -437,5 +437,14 @@ internal static string StreamDoesntSupportReading { return ResourceManager.GetString("StreamDoesntSupportReading", resourceCulture); } } + + /// + /// Looks up a localized string similar to Specified vector subtype {0} is not supported.. + /// + internal static string VectorSubTypeNotSupported { + get { + return ResourceManager.GetString("VectorSubTypeNotSupported", resourceCulture); + } + } } } diff --git a/src/Microsoft.Data.Analysis/Strings.resx b/src/Microsoft.Data.Analysis/Strings.resx index 79764037cc..53120a4a73 100644 --- a/src/Microsoft.Data.Analysis/Strings.resx +++ b/src/Microsoft.Data.Analysis/Strings.resx @@ -243,4 +243,8 @@ Stream doesn't support reading + + Specified vector subtype {0} is not supported. + {0} vectory subtype + \ No newline at end of file diff --git a/src/Microsoft.Data.Analysis/VBufferDataFrameColumn.cs b/src/Microsoft.Data.Analysis/VBufferDataFrameColumn.cs new file mode 100644 index 0000000000..caa786afdb --- /dev/null +++ b/src/Microsoft.Data.Analysis/VBufferDataFrameColumn.cs @@ -0,0 +1,397 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using Apache.Arrow; +using Apache.Arrow.Types; +using Microsoft.ML; +using Microsoft.ML.Data; + +namespace Microsoft.Data.Analysis +{ + /// + /// Column to hold VBuffer + /// + public partial class VBufferDataFrameColumn : DataFrameColumn, IEnumerable> + { + private readonly List>> _vBuffers = new List>>(); // To store more than intMax number of vbuffers + + /// + /// Constructs an empty VBufferDataFrameColumn with the given . + /// + /// The name of the column. + /// Length of values + public VBufferDataFrameColumn(string name, long length = 0) : base(name, length, typeof(VBuffer)) + { + int numberOfBuffersRequired = Math.Max((int)(length / int.MaxValue), 1); + for (int i = 0; i < numberOfBuffersRequired; i++) + { + long bufferLen = length - _vBuffers.Count * int.MaxValue; + List> buffer = new List>((int)Math.Min(int.MaxValue, bufferLen)); + _vBuffers.Add(buffer); + for (int j = 0; j < bufferLen; j++) + { + buffer.Add(default); + } + } + } + + public VBufferDataFrameColumn(string name, IEnumerable> values) : base(name, 0, typeof(VBuffer)) + { + values = values ?? throw new ArgumentNullException(nameof(values)); + if (_vBuffers.Count == 0) + { + _vBuffers.Add(new List>()); + } + foreach (var value in values) + { + Append(value); + } + } + + private long _nullCount; + + public override long NullCount => _nullCount; + + protected internal override void Resize(long length) + { + if (length < Length) + throw new ArgumentException(Strings.CannotResizeDown, nameof(length)); + + for (long i = Length; i < length; i++) + { + Append(default); + } + } + + public void Append(VBuffer value) + { + List> lastBuffer = _vBuffers[_vBuffers.Count - 1]; + if (lastBuffer.Count == int.MaxValue) + { + lastBuffer = new List>(); + _vBuffers.Add(lastBuffer); + } + lastBuffer.Add(value); + Length++; + } + + private int GetBufferIndexContainingRowIndex(ref long rowIndex) + { + if (rowIndex > Length) + { + throw new ArgumentOutOfRangeException(Strings.ColumnIndexOutOfRange, nameof(rowIndex)); + } + return (int)(rowIndex / int.MaxValue); + } + + protected override object GetValue(long rowIndex) + { + int bufferIndex = GetBufferIndexContainingRowIndex(ref rowIndex); + return _vBuffers[bufferIndex][(int)rowIndex]; + } + + protected override IReadOnlyList GetValues(long startIndex, int length) + { + var ret = new List(); + int bufferIndex = GetBufferIndexContainingRowIndex(ref startIndex); + while (ret.Count < length && bufferIndex < _vBuffers.Count) + { + for (int i = (int)startIndex; ret.Count < length && i < _vBuffers[bufferIndex].Count; i++) + { + ret.Add(_vBuffers[bufferIndex][i]); + } + bufferIndex++; + startIndex = 0; + } + return ret; + } + + protected override void SetValue(long rowIndex, object value) + { + if (value == null || value is VBuffer) + { + int bufferIndex = GetBufferIndexContainingRowIndex(ref rowIndex); + var oldValue = this[rowIndex]; + _vBuffers[bufferIndex][(int)rowIndex] = (VBuffer)value; + if (!oldValue.Equals((VBuffer)value)) + { + if (value == null) + _nullCount++; + if (oldValue.Length == 0 && _nullCount > 0) + _nullCount--; + } + } + else + { + throw new ArgumentException(string.Format(Strings.MismatchedValueType, typeof(VBuffer)), nameof(value)); + } + } + + public new VBuffer this[long rowIndex] + { + get => (VBuffer)GetValue(rowIndex); + set => SetValue(rowIndex, value); + } + + /// + /// Returns an enumerator that iterates through the VBuffer values in this column. + /// + public IEnumerator> GetEnumerator() + { + foreach (List> buffer in _vBuffers) + { + foreach (VBuffer value in buffer) + { + yield return value; + } + } + } + + /// + protected override IEnumerator GetEnumeratorCore() => GetEnumerator(); + + /// + protected internal override void AddDataViewColumn(DataViewSchema.Builder builder) + { + builder.AddColumn(Name, GetDataViewType()); + } + + /// + protected internal override Delegate GetDataViewGetter(DataViewRowCursor cursor) + { + return CreateValueGetterDelegate(cursor); + } + + private ValueGetter> CreateValueGetterDelegate(DataViewRowCursor cursor) => + (ref VBuffer value) => value = this[cursor.Position]; + + public override Dictionary> GetGroupedOccurrences(DataFrameColumn other, out HashSet otherColumnNullIndices) + { + return GetGroupedOccurrences(other, out otherColumnNullIndices); + } + + protected internal override Delegate GetValueGetterUsingCursor(DataViewRowCursor cursor, DataViewSchema.Column schemaColumn) + { + return cursor.GetGetter>(schemaColumn); + } + + protected internal override void AddValueUsingCursor(DataViewRowCursor cursor, Delegate getter) + { + long row = cursor.Position; + VBuffer value = default; + Debug.Assert(getter != null, "Excepted getter to be valid"); + + (getter as ValueGetter>)(ref value); + + if (Length > row) + { + this[row] = value; + } + else if (Length == row) + { + Append(value); + } + else + { + throw new IndexOutOfRangeException(nameof(row)); + } + } + + private VBufferDataFrameColumn Clone(PrimitiveDataFrameColumn boolColumn) + { + if (boolColumn.Length > Length) + throw new ArgumentException(Strings.MapIndicesExceedsColumnLenth, nameof(boolColumn)); + VBufferDataFrameColumn ret = new VBufferDataFrameColumn(Name, 0); + for (long i = 0; i < boolColumn.Length; i++) + { + bool? value = boolColumn[i]; + if (value.HasValue && value.Value == true) + ret.Append(this[i]); + } + return ret; + } + + private VBufferDataFrameColumn Clone(PrimitiveDataFrameColumn mapIndices = null, bool invertMapIndex = false) + { + if (mapIndices is null) + { + VBufferDataFrameColumn ret = new VBufferDataFrameColumn(Name, Length); + for (long i = 0; i < Length; i++) + { + ret[i] = this[i]; + } + return ret; + } + else + { + return CloneImplementation(mapIndices, invertMapIndex); + } + } + + private VBufferDataFrameColumn Clone(PrimitiveDataFrameColumn mapIndices, bool invertMapIndex = false) + { + return CloneImplementation(mapIndices, invertMapIndex); + } + + private VBufferDataFrameColumn CloneImplementation(PrimitiveDataFrameColumn mapIndices, bool invertMapIndices = false, long numberOfNullsToAppend = 0) + where U : unmanaged + { + mapIndices = mapIndices ?? throw new ArgumentNullException(nameof(mapIndices)); + VBufferDataFrameColumn ret = new VBufferDataFrameColumn(Name, mapIndices.Length); + + List> setBuffer = ret._vBuffers[0]; + long setBufferMinRange = 0; + long setBufferMaxRange = int.MaxValue; + List> getBuffer = _vBuffers[0]; + long getBufferMinRange = 0; + long getBufferMaxRange = int.MaxValue; + long maxCapacity = int.MaxValue; + if (mapIndices.DataType == typeof(long)) + { + PrimitiveDataFrameColumn longMapIndices = mapIndices as PrimitiveDataFrameColumn; + longMapIndices.ApplyElementwise((long? mapIndex, long rowIndex) => + { + long index = rowIndex; + if (invertMapIndices) + index = longMapIndices.Length - 1 - index; + if (index < setBufferMinRange || index >= setBufferMaxRange) + { + int bufferIndex = (int)(index / maxCapacity); + setBuffer = ret._vBuffers[bufferIndex]; + setBufferMinRange = bufferIndex * maxCapacity; + setBufferMaxRange = (bufferIndex + 1) * maxCapacity; + } + index -= setBufferMinRange; + + if (mapIndex.Value < getBufferMinRange || mapIndex.Value >= getBufferMaxRange) + { + int bufferIndex = (int)(mapIndex.Value / maxCapacity); + getBuffer = _vBuffers[bufferIndex]; + getBufferMinRange = bufferIndex * maxCapacity; + getBufferMaxRange = (bufferIndex + 1) * maxCapacity; + } + int bufferLocalMapIndex = (int)(mapIndex - getBufferMinRange); + VBuffer value = getBuffer[bufferLocalMapIndex]; + setBuffer[(int)index] = value; + + return mapIndex; + }); + } + else if (mapIndices.DataType == typeof(int)) + { + PrimitiveDataFrameColumn intMapIndices = mapIndices as PrimitiveDataFrameColumn; + intMapIndices.ApplyElementwise((int? mapIndex, long rowIndex) => + { + long index = rowIndex; + if (invertMapIndices) + index = intMapIndices.Length - 1 - index; + + VBuffer value = getBuffer[mapIndex.Value]; + setBuffer[(int)index] = value; + + return mapIndex; + }); + } + else + { + Debug.Assert(false, nameof(mapIndices.DataType)); + } + + return ret; + } + + public new VBufferDataFrameColumn Clone(DataFrameColumn mapIndices, bool invertMapIndices, long numberOfNullsToAppend) + { + VBufferDataFrameColumn clone; + if (!(mapIndices is null)) + { + Type dataType = mapIndices.DataType; + if (dataType != typeof(long) && dataType != typeof(int) && dataType != typeof(bool)) + throw new ArgumentException(String.Format(Strings.MultipleMismatchedValueType, typeof(long), typeof(int), typeof(bool)), nameof(mapIndices)); + if (mapIndices.DataType == typeof(long)) + clone = Clone(mapIndices as PrimitiveDataFrameColumn, invertMapIndices); + else if (dataType == typeof(int)) + clone = Clone(mapIndices as PrimitiveDataFrameColumn, invertMapIndices); + else + clone = Clone(mapIndices as PrimitiveDataFrameColumn); + } + else + { + clone = Clone(); + } + + return clone; + } + + protected override DataFrameColumn CloneImplementation(DataFrameColumn mapIndices = null, bool invertMapIndices = false, long numberOfNullsToAppend = 0) + { + return Clone(mapIndices, invertMapIndices, numberOfNullsToAppend); + } + + private static VectorDataViewType GetDataViewType() + { + if (typeof(T) == typeof(bool)) + { + return new VectorDataViewType(BooleanDataViewType.Instance); + } + else if (typeof(T) == typeof(byte)) + { + return new VectorDataViewType(NumberDataViewType.Byte); + } + else if (typeof(T) == typeof(double)) + { + return new VectorDataViewType(NumberDataViewType.Double); + } + else if (typeof(T) == typeof(float)) + { + return new VectorDataViewType(NumberDataViewType.Single); + } + else if (typeof(T) == typeof(int)) + { + return new VectorDataViewType(NumberDataViewType.Int32); + } + else if (typeof(T) == typeof(long)) + { + return new VectorDataViewType(NumberDataViewType.Int64); + } + else if (typeof(T) == typeof(sbyte)) + { + return new VectorDataViewType(NumberDataViewType.SByte); + } + else if (typeof(T) == typeof(short)) + { + return new VectorDataViewType(NumberDataViewType.Int16); + } + else if (typeof(T) == typeof(uint)) + { + return new VectorDataViewType(NumberDataViewType.UInt32); + } + else if (typeof(T) == typeof(ulong)) + { + return new VectorDataViewType(NumberDataViewType.UInt64); + } + else if (typeof(T) == typeof(ushort)) + { + return new VectorDataViewType(NumberDataViewType.UInt16); + } + else if (typeof(T) == typeof(char)) + { + return new VectorDataViewType(NumberDataViewType.UInt16); + } + else if (typeof(T) == typeof(decimal)) + { + return new VectorDataViewType(NumberDataViewType.Double); + } + + throw new NotSupportedException(); + } + } +} diff --git a/src/Microsoft.ML.AutoML/API/AutoMLExperimentExtension.cs b/src/Microsoft.ML.AutoML/API/AutoMLExperimentExtension.cs index f16afac17e..b2437588d0 100644 --- a/src/Microsoft.ML.AutoML/API/AutoMLExperimentExtension.cs +++ b/src/Microsoft.ML.AutoML/API/AutoMLExperimentExtension.cs @@ -254,10 +254,11 @@ public static AutoMLExperiment SetGridSearchTuner(this AutoMLExperiment experime return tuner; }); - + return experiment; } + /// /// Set checkpoint folder for . The checkpoint folder will be used to save /// temporary output, run history and many other stuff which will be used for restoring training process /// from last checkpoint and continue training. diff --git a/test/Microsoft.Data.Analysis.Tests/DataFrameIDataViewTests.cs b/test/Microsoft.Data.Analysis.Tests/DataFrameIDataViewTests.cs index a7dceb8774..4576870707 100644 --- a/test/Microsoft.Data.Analysis.Tests/DataFrameIDataViewTests.cs +++ b/test/Microsoft.Data.Analysis.Tests/DataFrameIDataViewTests.cs @@ -3,10 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Linq; +using Microsoft.Data.Analysis.Tests; using Microsoft.ML; using Microsoft.ML.Data; using Xunit; +using Microsoft.ML.Trainers; namespace Microsoft.Data.Analysis.Tests { @@ -19,7 +22,7 @@ public void TestIDataView() DataDebuggerPreview preview = dataView.Preview(); Assert.Equal(10, preview.RowView.Length); - Assert.Equal(16, preview.ColumnView.Length); + Assert.Equal(17, preview.ColumnView.Length); Assert.Equal("Byte", preview.ColumnView[0].Column.Name); Assert.Equal((byte)0, preview.ColumnView[0].Values[0]); @@ -84,6 +87,10 @@ public void TestIDataView() Assert.Equal("ArrowString", preview.ColumnView[15].Column.Name); Assert.Equal("foo".ToString(), preview.ColumnView[15].Values[0].ToString()); Assert.Equal("foo".ToString(), preview.ColumnView[15].Values[1].ToString()); + + Assert.Equal("VBuffer", preview.ColumnView[16].Column.Name); + Assert.Equal("Dense vector of size 5", preview.ColumnView[16].Values[0].ToString()); + Assert.Equal("Dense vector of size 5", preview.ColumnView[16].Values[1].ToString()); } [Fact] @@ -121,7 +128,7 @@ public void TestIDataViewWithNulls() DataDebuggerPreview preview = dataView.Preview(); Assert.Equal(length, preview.RowView.Length); - Assert.Equal(16, preview.ColumnView.Length); + Assert.Equal(17, preview.ColumnView.Length); Assert.Equal("Byte", preview.ColumnView[0].Column.Name); Assert.Equal((byte)0, preview.ColumnView[0].Values[0]); @@ -234,12 +241,16 @@ public void TestIDataViewWithNulls() Assert.Equal("foo", preview.ColumnView[15].Values[4].ToString()); Assert.Equal("", preview.ColumnView[15].Values[5].ToString()); // null row Assert.Equal("foo", preview.ColumnView[15].Values[6].ToString()); + + Assert.Equal("VBuffer", preview.ColumnView[16].Column.Name); + Assert.True(preview.ColumnView[16].Values[0] is VBuffer); + Assert.True(preview.ColumnView[16].Values[6] is VBuffer); } [Fact] public void TestDataFrameFromIDataView() { - DataFrame df = DataFrameTests.MakeDataFrameWithAllColumnTypes(10, withNulls: false); + DataFrame df = DataFrameTests.MakeDataFrameWithAllMutableAndArrowColumnTypes(10, withNulls: false); df.Columns.Remove("Char"); // Because chars are returned as uint16 by IDataView, so end up comparing CharDataFrameColumn to UInt16DataFrameColumn and fail asserts IDataView dfAsIDataView = df; DataFrame newDf = dfAsIDataView.ToDataFrame(); @@ -430,5 +441,48 @@ public void TestDataFrameFromIDataView_MLData_SelectColumnsAndRows() VerifyDataFrameColumnAndDataViewColumnValues("Name", data, df, 3); VerifyDataFrameColumnAndDataViewColumnValues("Value", data, df, 3); } + + [Fact] + public void TestDataFrameFromIDataView_VBufferType() + { + var mlContext = new MLContext(); + + var inputData = new[] + { + new { + boolFeature = new bool[] {false, false}, + byteFeatures = new byte[] {0, 0}, + doubleFeatures = new double[] {0, 0}, + floatFeatures = new float[] {0, 0}, + intFeatures = new int[] {0, 0}, + longFeatures = new long[] {0, 0}, + sbyteFeatures = new sbyte[] {0, 0}, + shortFeatures = new short[] {0, 0}, + ushortFeatures = new ushort[] {0, 0}, + uintFeatures = new uint[] {0, 0}, + ulongFeatures = new ulong[] {0, 0}, + }, + new { + boolFeature = new bool[] {false, false}, + byteFeatures = new byte[] {0, 0}, + doubleFeatures = new double[] {0, 0}, + floatFeatures = new float[] {1, 1}, + intFeatures = new int[] {0, 0}, + longFeatures = new long[] {0, 0}, + sbyteFeatures = new sbyte[] {0, 0}, + shortFeatures = new short[] {0, 0}, + ushortFeatures = new ushort[] {0, 0}, + uintFeatures = new uint[] {0, 0}, + ulongFeatures = new ulong[] {0, 0}, + } + }; + + var data = mlContext.Data.LoadFromEnumerable(inputData); + var df = data.ToDataFrame(); + + Assert.Equal(11, df.Columns.Count); + Assert.Equal(2, df.Rows.Count); + } } } + diff --git a/test/Microsoft.Data.Analysis.Tests/DataFrameTests.cs b/test/Microsoft.Data.Analysis.Tests/DataFrameTests.cs index 31263b4eff..06286b3ae4 100644 --- a/test/Microsoft.Data.Analysis.Tests/DataFrameTests.cs +++ b/test/Microsoft.Data.Analysis.Tests/DataFrameTests.cs @@ -8,6 +8,7 @@ using System.Text; using Apache.Arrow; using Microsoft.ML; +using Microsoft.ML.Data; using Xunit; namespace Microsoft.Data.Analysis.Tests @@ -74,11 +75,28 @@ public static ArrowStringDataFrameColumn CreateArrowStringColumn(int length, boo return new ArrowStringDataFrameColumn("ArrowString", dataMemory, offsetMemory, nullMemory, length, nullCount); } + public static VBufferDataFrameColumn CreateVBufferDataFrame(int length) + { + var buffers = Enumerable.Repeat(new VBuffer(5, new[] { 0, 1, 2, 3, 4 }), length).ToArray(); + return new VBufferDataFrameColumn("VBuffer", buffers); + } + public static DataFrame MakeDataFrameWithAllColumnTypes(int length, bool withNulls = true) + { + DataFrame df = MakeDataFrameWithAllMutableAndArrowColumnTypes(length, withNulls); + + var vBufferColumn = CreateVBufferDataFrame(length); + df.Columns.Insert(df.Columns.Count, vBufferColumn); + + return df; + } + + public static DataFrame MakeDataFrameWithAllMutableAndArrowColumnTypes(int length, bool withNulls = true) { DataFrame df = MakeDataFrameWithAllMutableColumnTypes(length, withNulls); DataFrameColumn arrowStringColumn = CreateArrowStringColumn(length, withNulls); df.Columns.Insert(df.Columns.Count, arrowStringColumn); + return df; } @@ -211,6 +229,15 @@ public DataFrame SplitTrainTest(DataFrame input, float testRatio, out DataFrame return input[trainIndices]; } + [Fact] + public void TestVBufferColumn() + { + var vBufferColumn = CreateVBufferDataFrame(10); + + Assert.Equal(10, vBufferColumn.Length); + Assert.Equal(5, vBufferColumn[0].GetValues().Length); + Assert.Equal(0, vBufferColumn[0].GetValues()[0]); + } [Fact] public void TestIndexer() diff --git a/test/Microsoft.Data.Analysis.Tests/Microsoft.Data.Analysis.Tests.csproj b/test/Microsoft.Data.Analysis.Tests/Microsoft.Data.Analysis.Tests.csproj index 07c3ea2c33..872769ef74 100644 --- a/test/Microsoft.Data.Analysis.Tests/Microsoft.Data.Analysis.Tests.csproj +++ b/test/Microsoft.Data.Analysis.Tests/Microsoft.Data.Analysis.Tests.csproj @@ -32,7 +32,7 @@ - +