在 C# 中使用 Deedle 连接两个字符串列

Concatenating two string columns using Deedle in C#

函数add_ints添加两个整数列

A,B
2,3
5,7
9,11

在 CSV 文件中。

为什么函数 add_strings 不能正确连接两个字符串列

L,R
"a","b"
"c","d"
"e","f"

进入第三列

L,R,C
"a","b","ab"
"c","d","cd"
"e","f","ef"

从类似的 CSV 文件开始时?

using Deedle;
using System.IO;

namespace NS
{
    class TwoColumnOps
    {
        static void Main(string[] args)
        {
            string root = "path/to";
            add_ints(root);
            add_strings(root);
        }
        static void add_ints(string root)
        {
            Deedle.Frame<int, string> df = Frame.ReadCsv(Path.Combine(root, "data_ints.csv"));

            Series<int, int> a = df.GetColumn<int>("A");
            Series<int, int> b = df.GetColumn<int>("B");

            Series<int, int> c = a + b;
            df.AddColumn("C", c);
            df.Print();
        }
        static void add_strings(string root)
        {
            Deedle.Frame<int, string> df = Frame.ReadCsv(Path.Combine(root, "data_strings.csv"));

            Series<int, string> a = df.GetColumn<string>("L");
            Series<int, string> b = df.GetColumn<string>("R");

            // Series<int, string> c = a + b;
            // Series<int, string> c = $"{a} and {b}";
            Series<int, string> c = string.Concat(a, b);

            df.AddColumn("C", c);
            df.Print();
        }
    }
}

所有三种连接方式的错误是:

Error   CS0029  Cannot implicitly convert type 'string' to 'Deedle.Series<int, string>' 

我从未使用过 deedle,但您的数据是两个字符串列。两列都包含字符串数据而不是数字,所以看起来这一行:

Deedle.Frame<int, string> df = Frame.ReadCsv(Path.Combine(root, "data_strings.csv"));

应该是:

Deedle.Frame<string, string> df = Frame.ReadCsv(Path.Combine(root, "data_strings.csv"));

查看此处的文档:https://bluemountaincapital.github.io/Deedle/csharpframe.html 他们说 Deedle 推断数据类型,并且在他们所有的示例中他们只是使用 'var' 而不是显式类型。尝试使用:

var df = Frame.ReadCsv(Path.Combine(root, "data_strings.csv"));

然后你就可以用调试器调试看看 df 是什么样子了。祝你好运!

对于第一个答案的混淆,我们深表歉意。似乎没有什么好的方法可以将系列添加在一起。我尝试了“合并”方法,但它抛出了错误。我在本地重新创建了它,虽然它看起来有点老套,但它有效...

static void add_strings(string root)
    {
        Deedle.Frame<int, string> df = Frame.ReadCsv("data_strings.csv");

        Series<int, string> a = df.GetColumn<string>("L");
        Series<int, string> b = df.GetColumn<string>("R");

        // Series<int, string> c = a + b;
        // Series<int, string> c = $"{a} and {b}";
        int rowCount = a.ValueCount + b.ValueCount;
        int[] keys = Enumerable.Range(0, rowCount).ToArray();
        Series<int, string> c = new Series<int, string>(keys, a.Values.Concat(b.Values));

        df.AddColumn("C", c);
        df.Print();
    }

第三次是一种魅力,希望如此。请参阅匹配输出的屏幕截图。我不喜欢迭代方法,但结果是正确的。我试图查看是否有任何方法或扩展可以工作,但发现 none。从好的方面来说,这为您想要对每一行进行缩放、串联等以构建新列的任何更改打开了大门。希望对您有所帮助。

static void add_strings(string root)
    {
        Deedle.Frame<int, string> df = Frame.ReadCsv("data_strings.csv");

        Series<int, string> a = df.GetColumn<string>("L");
        Series<int, string> b = df.GetColumn<string>("R");

        RowSeries<int, string> rs = df.Rows;

        SeriesBuilder<int, string> c = new SeriesBuilder<int, string>();
        for (int i = 0; i < rs.KeyCount; i++)
        {
            c.Add(i, a[i] + b[i]);
        }

        df.AddColumn("C", c);
        df.Print();
    }

对于提供多个答案,我深表歉意,我对尝试参与和提供答案还是个新手。 FWIW:鉴于 zyzhu 的新评论引用了新的过载;我想我会提供另一种解决方案来帮助您。我认为重载字符串的“+”运算符将是一个很好的补充。我还认为这里还有很多需要改进的地方,可以通过创建一个 mutator 方法并采用委托来允许用户定义 mutation 来实现。用户可能想要的不仅仅是简单的突变,还可能想要进行一些实际的计算或其他更改。考虑这个扩展方法及其示例,请原谅缺乏错误检查或对原始类型以外的任何支持...

public static class FrameMutator
{
    /// <summary>
    /// For a frame of type Frame<TRow,TCol> mutate its rows of type TVal and create a new column with the results
    /// </summary>
    /// <typeparam name="TRow">Row Type</typeparam>
    /// <typeparam name="TVal">Value Type</typeparam>
    /// <typeparam name="TCol">Column Type</typeparam>
    /// <param name="myFrame"></param>
    /// <param name="mutatorMethod">delegate for transformation</param>
    /// <returns>Series<K, V></returns>
    public static Series<TRow, TVal> Mutate<TRow,TVal,TCol>(this Frame<TRow, TCol> myFrame, Func<TVal[], TVal> mutatorMethod)
    {
        SeriesBuilder<TRow, TVal> result = new SeriesBuilder<TRow, TVal>();
        foreach (TRow key in myFrame.Rows.Keys)
        {
            TVal colResult = mutatorMethod(myFrame.Rows[key].GetValues<TVal>().ToArray());
            result.Add(key, colResult);
        }

        return result.ToSeries();
    }
}

此扩展可按如下方式使用...

static void add_ints(string root)
    {
        Deedle.Frame<int, string> df = Frame.ReadCsv("data_ints.csv");

        Series<int, int> a = df.GetColumn<int>("A");
        Series<int, int> b = df.GetColumn<int>("B");

        //creates a column with the average of the row (not so useful with int)
        Series<int, int> avgCol = df.Mutate<int, int, string>(avgMutator);
        Series<int, int> c = a + b;

        df.AddColumn("C", c);
        df.AddColumn("D", avgCol);
        df.Print();
    }
    static void add_strings(string root)
    {
        Deedle.Frame<int, string> df = Frame.ReadCsv("data_strings.csv");

        Series<int, string> a = df.GetColumn<string>("L");
        Series<int, string> b = df.GetColumn<string>("R");

        //creates a column of concatenanted values
        Series<int,string> concatCol = df.Mutate<int,string,string>(ConcatMutator);
        //creates a column of concatenated and UPPER values
        Series<int, string> upperCol = df.Mutate<int, string, string>(ToUpperMutator);

        df.AddColumn("C", concatCol);
        df.AddColumn("D", upperCol);

        df.Print();
    }      

    private static string ConcatMutator(string[] inputs) => string.Concat(inputs);

    private static string ToUpperMutator(string[] inputs)
    {
        IEnumerable<string> uppers = inputs.Select(e => e.ToUpper());
        return string.Concat(uppers);
    }
    private static int avgMutator(int[] inputs) => (int)Math.Round(inputs.Average(), 0);

+ 对数字系列起作用,而 string.Concat 对字符串系列不起作用的原因是系列类型为数字系列定义了一个重载的 + 运算符。遗憾的是,这只适用于数字。

对于非数字系列,最简单的选择是使用 ZipInner 对齐两个系列。这为您提供了一系列元组。然后,您可以使用 Select 以元素方式转换值:

var df = Frame.ReadCsv("/some/test/file.csv");
var s1 = df.GetColumn<string>("first");
var s2 = df.GetColumn<string>("second");
var added = s1.ZipInner(s2).Select(t => t.Value.Item1 + t.Value.Item2);
df.AddColumn("added", added);

使用最新的 Deedle 2.1.0。 + 被系列和标量、系列和系列、系列和框架中的字符串连接覆盖。 Frame.strConcat 适用于字符串值的框架 https://github.com/fslaborg/Deedle/pull/483

您的代码现在应该可以工作了。

Deedle.Frame<int, string> df = Frame.ReadCsv(Path.Combine(root, "data_strings.csv"));

Series<int, string> a = df.GetColumn<string>("L");
Series<int, string> b = df.GetColumn<string>("R");
Series<int, string> c = a + b;