File: DataDebuggerPreview.cs
Web Access
Project: src\src\Microsoft.ML.Data\Microsoft.ML.Data.csproj (Microsoft.ML.Data)
// 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.Collections.Immutable;
using System.Linq;
using Microsoft.ML.Internal.Utilities;
using Microsoft.ML.Runtime;
 
namespace Microsoft.ML.Data
{
    /// <summary>
    /// This class represents an eager 'preview' of a <see cref="IDataView"/>.
    /// </summary>
    public sealed class DataDebuggerPreview
    {
        private static readonly FuncInstanceMethodInfo1<DataDebuggerPreview, DataViewRow, int, Action<RowInfo, List<object>>> _makeSetterMethodInfo
            = FuncInstanceMethodInfo1<DataDebuggerPreview, DataViewRow, int, Action<RowInfo, List<object>>>.Create(target => target.MakeSetter<int>);
 
        internal static class Defaults
        {
            public const int MaxRows = 100;
        }
 
        public DataViewSchema Schema { get; }
        public ImmutableArray<ColumnInfo> ColumnView { get; }
        public ImmutableArray<RowInfo> RowView { get; }
 
        internal DataDebuggerPreview(IDataView data, int maxRows = Defaults.MaxRows)
        {
            Contracts.CheckValue(data, nameof(data));
            Contracts.CheckParam(maxRows >= 0, nameof(maxRows));
            Schema = data.Schema;
 
            int n = data.Schema.Count;
 
            var rows = new List<RowInfo>();
            var columns = new List<object>[n];
            for (int i = 0; i < columns.Length; i++)
                columns[i] = new List<object>();
 
            using (var cursor = data.GetRowCursorForAllColumns())
            {
                var setters = new Action<RowInfo, List<object>>[n];
                for (int i = 0; i < n; i++)
                    setters[i] = Utils.MarshalInvoke(_makeSetterMethodInfo, this, data.Schema[i].Type.RawType, cursor, i);
 
                int count = 0;
                while (count < maxRows && cursor.MoveNext())
                {
                    var curRow = new RowInfo(n);
                    for (int i = 0; i < setters.Length; i++)
                        setters[i](curRow, columns[i]);
 
                    rows.Add(curRow);
                    count++;
                }
            }
            RowView = rows.ToImmutableArray();
            ColumnView = Enumerable.Range(0, n).Select(c => new ColumnInfo(data.Schema[c], columns[c].ToArray())).ToImmutableArray();
        }
 
        public override string ToString()
            => $"{Schema.Count} columns, {RowView.Length} rows";
 
        private Action<RowInfo, List<object>> MakeSetter<T>(DataViewRow row, int col)
        {
            var column = row.Schema[col];
            var getter = row.GetGetter<T>(column);
            Action<RowInfo, List<object>> result = (rowInfo, list) =>
            {
                T value = default;
                getter(ref value);
                rowInfo.Values[col] = new KeyValuePair<string, object>(column.Name, value);
 
                // Call getter again on another buffer, since we store it in two places.
                value = default;
                getter(ref value);
                list.Add(value);
            };
            return result;
        }
 
        public sealed class RowInfo
        {
            [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
            public KeyValuePair<string, object>[] Values { get; }
 
            public override string ToString()
                => $"{Values.Length} columns";
 
            internal RowInfo(int n)
            {
                Values = new KeyValuePair<string, object>[n];
            }
        }
 
        public sealed class ColumnInfo
        {
            public DataViewSchema.Column Column { get; }
            public object[] Values { get; }
 
            public override string ToString() => $"{Column.Name}: {Column.Type}";
 
            internal ColumnInfo(DataViewSchema.Column column, object[] values)
            {
                Column = column;
                Values = values;
            }
        }
    }
}