ML.NET 以通用方式合并 IDataView

ML.NET Merge IDataViews in a genric way

我正在为 ML.NET 创建一个 CLI 工具,我需要创建一个合并函数,以合并两个相同类型的数据集。但它需要尽可能通用,因为该工具包含不同的数据集类型。

我设法创建了一个方法,将 IDataview 转换为具有给定数据集类型的通用 IEnumerable:

private IEnumerable<object> GetDataEnumerable(MLContext mlContext, IDataView dataView, Type dataViewType) {
        var createEnumerableMethod = typeof(DataOperationsCatalog).GetMethod(nameof(MLContext.Data.CreateEnumerable));
        var generic = createEnumerableMethod.MakeGenericMethod(dataViewType);
        return (IEnumerable<object>)generic.Invoke(mlContext.Data, new object[] { dataView, false, null, null });
    }

几天后我想通了。首先,我将 2 个 IDataviews 转换为在运行时获取类型的 IEnumerable。之后,我将它们合并在一起,将它们转换为列表,并使用反射将它们转换为正确的类型。最后,我再次使用反射将最终可枚举加载回 IDataview。

    var oldDataEnumerable = GetDataEnumerable(mlContext, oldDataView, dataViewType).ToList();
    var newDataEnumerable = GetDataEnumerable(mlContext, newDataView, dataViewType).ToList();

    var mergedEnumerable = MergeDataEnumerables(oldDataEnumerable, newDataEnumerable, dataViewType);

    return LoadEnumerableFromObject(mlContext, mergedEnumerable, dataViewType);

    private IEnumerable<object> GetDataEnumerable(MLContext mlContext, IDataView dataView, Type dataViewType) {
        var createEnumerableMethod = typeof(DataOperationsCatalog).GetMethod(nameof(MLContext.Data.CreateEnumerable));
        var generic = createEnumerableMethod.MakeGenericMethod(dataViewType);
        return (IEnumerable<object>)generic.Invoke(mlContext.Data, new object[] { dataView, false, null, null });
    }

    private object MergeDataEnumerables(List<object> firstDataEnumerable, List<object> secondDataEnumerable, Type dataViewType) {
        firstDataEnumerable.AddRange(secondDataEnumerable);
        var castMethod = typeof(Enumerable).GetMethod("Cast");
        var genericCast = castMethod.MakeGenericMethod(dataViewType);
        var toListMethod = typeof(Enumerable).GetMethod("ToList");
        var genericToList = toListMethod.MakeGenericMethod(dataViewType);
        var castedEnumerable = genericCast.Invoke(null, new[] { firstDataEnumerable });
        return genericToList.Invoke(null, new[] { castedEnumerable });
    }

    private IDataView LoadEnumerableFromObject(MLContext mlContext, object dataEnumerable, Type dataViewType) {
        var dataType = mlContext.Data.GetType();
        var loadFromEnumerableMethod = dataType.GetMethods().First(m => m.Name == "LoadFromEnumerable" && m.IsGenericMethod);
        var generic = loadFromEnumerableMethod.MakeGenericMethod(dataViewType);
        var schema = SchemaDefinition.Create(dataViewType);
        return (IDataView)generic.Invoke(mlContext.Data, new object[] { dataEnumerable, schema });
    }