Объединение двух строковых столбцов с использованием Deedle в C # - PullRequest
2 голосов
/ 18 октября 2019

Функция 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>' 

Ответы [ 6 ]

1 голос
/ 19 октября 2019

Причина, по которой + работает с сериями чисел, но 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);
0 голосов
/ 08 ноября 2019

С последней версией 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;
0 голосов
/ 18 октября 2019

Я прошу прощения за предоставление нескольких ответов, я все еще новичок в попытках скинуться и предложить ответы. FWIW: в свете нового комментария от zyzhu со ссылкой на добавление новой перегрузки;Я думал, что предложу еще одно решение, чтобы помочь вам. Я думаю, что перегрузка оператора '+' для строки будет хорошим дополнением. Я также думаю, что здесь нужно гораздо больше, чего можно достичь, создав метод мутатора и получив делегата, который позволит пользователю определить мутацию. Вполне возможно, что пользователь может захотеть больше, чем простые мутации, и может захотеть сделать некоторые реальные вычисления или другие изменения. Рассмотрим этот метод расширения и его примеры, и, пожалуйста, извините за отсутствие проверки ошибок или поддержки чего-либо, кроме примитивных типов ...

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);
0 голосов
/ 18 октября 2019

Третий раз - это очарование, надеюсь. Смотрите на скриншот для соответствия выходных данных. Я не предпочитаю итеративный подход, но результат правильный. Я пытался посмотреть, будут ли работать какие-либо методы или расширения, но не нашел ни одного. С другой стороны, это открывает дверь для любой мутации, которую вы хотите масштабировать, объединять и т. Д. Для каждой строки, чтобы построить новый столбец. Надеюсь, это поможет.

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();
    }

enter image description here

0 голосов
/ 18 октября 2019

Извините за путаницу при первом ответе. Кажется, нет хорошего способа добавить серию вместе. Я попробовал метод «Слияние», и он выдавал ошибки. Я воссоздал это локально, и, хотя это выглядит немного странно, это работает ...

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();
    }

enter image description here

0 голосов
/ 18 октября 2019

Я никогда не использовал 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 с отладчиком. Удачи!

...