我正在为 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 }); }
回答:
经过几天的努力,我终于找到了解决方案。首先,我将两个 IDataView 转换为在运行时获取类型的 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 }); }