Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
09/12/11 13:48:31 (13 years ago)
Author:
mkommend
Message:

#1597, #1609, #1640:

  • Corrected TableFileParser to handle empty rows correctly.
  • Refactored DataSet to store values in List<List> instead of a two-dimensional array.
  • Enable importing and storing string and datetime values.
  • Changed data access methods in dataset and adapted all concerning classes.
  • Changed interpreter to store the variable values for all rows during the compilation step.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Problems.DataAnalysis/3.4/TableFileParser.cs

    r5809 r6740  
    2121
    2222using System;
     23using System.Collections;
    2324using System.Collections.Generic;
    2425using System.Globalization;
     
    3334    private readonly char[] POSSIBLE_SEPARATORS = new char[] { ',', ';', '\t' };
    3435    private Tokenizer tokenizer;
    35     private List<List<double>> rowValues;
     36    private List<List<object>> rowValues;
    3637
    3738    private int rows;
     
    4748    }
    4849
    49     private double[,] values;
    50     public double[,] Values {
     50    private List<IList> values;
     51    public List<IList> Values {
    5152      get {
    5253        return values;
     
    6970
    7071    public TableFileParser() {
    71       rowValues = new List<List<double>>();
     72      rowValues = new List<List<object>>();
    7273      variableNames = new List<string>();
    7374    }
     
    7576    public void Parse(string fileName) {
    7677      NumberFormatInfo numberFormat;
     78      DateTimeFormatInfo dateTimeFormatInfo;
    7779      char separator;
    78       DetermineFileFormat(fileName, out numberFormat, out separator);
     80      DetermineFileFormat(fileName, out numberFormat, out dateTimeFormatInfo, out separator);
    7981      using (StreamReader reader = new StreamReader(fileName)) {
    80         tokenizer = new Tokenizer(reader, numberFormat, separator);
     82        tokenizer = new Tokenizer(reader, numberFormat, dateTimeFormatInfo, separator);
    8183        // parse the file
    8284        Parse();
     
    8688      rows = rowValues.Count;
    8789      columns = rowValues[0].Count;
    88       values = new double[rows, columns];
    89 
    90       int rowIndex = 0;
    91       int columnIndex = 0;
    92       foreach (List<double> row in rowValues) {
    93         columnIndex = 0;
    94         foreach (double element in row) {
    95           values[rowIndex, columnIndex++] = element;
    96         }
    97         rowIndex++;
    98       }
    99     }
    100 
    101     private void DetermineFileFormat(string fileName, out NumberFormatInfo numberFormat, out char separator) {
     90      values = new List<IList>();
     91
     92      //create columns
     93      for (int col = 0; col < columns; col++) {
     94        var types = rowValues.Select(r => r[col]).Where(v => v != null && v as string != string.Empty).Take(10).Select(v => v.GetType());
     95        if (!types.Any()) {
     96          values.Add(new List<string>());
     97          continue;
     98        }
     99
     100        var columnType = types.GroupBy(v => v).OrderBy(v => v).Last().Key;
     101        if (columnType == typeof(double)) values.Add(new List<double>());
     102        else if (columnType == typeof(DateTime)) values.Add(new List<DateTime>());
     103        else if (columnType == typeof(string)) values.Add(new List<string>());
     104        else throw new InvalidOperationException();
     105      }
     106
     107
     108
     109      //fill with values
     110      foreach (List<object> row in rowValues) {
     111        int columnIndex = 0;
     112        foreach (object element in row) {
     113          //handle missing values with default values
     114          if (element as string == string.Empty) {
     115            if (values[columnIndex] is List<double>) values[columnIndex].Add(double.NaN);
     116            else if (values[columnIndex] is List<DateTime>) values[columnIndex].Add(DateTime.MinValue);
     117            else if (values[columnIndex] is List<string>) values[columnIndex].Add(string.Empty);
     118            else throw new InvalidOperationException();
     119          } else values[columnIndex].Add(element);
     120          columnIndex++;
     121        }
     122      }
     123    }
     124
     125    private void DetermineFileFormat(string fileName, out NumberFormatInfo numberFormat, out DateTimeFormatInfo dateTimeFormatInfo, out char separator) {
    102126      using (StreamReader reader = new StreamReader(fileName)) {
    103127        // skip first line
     
    123147        if (OccurrencesOf(charCounts, '.') > 10) {
    124148          numberFormat = NumberFormatInfo.InvariantInfo;
     149          dateTimeFormatInfo = DateTimeFormatInfo.InvariantInfo;
    125150          separator = POSSIBLE_SEPARATORS
    126151            .Where(c => OccurrencesOf(charCounts, c) > 10)
     
    139164            // English format (only integer values) with ',' as separator
    140165            numberFormat = NumberFormatInfo.InvariantInfo;
     166            dateTimeFormatInfo = DateTimeFormatInfo.InvariantInfo;
    141167            separator = ',';
    142168          } else {
     
    144170            // German format (real values)
    145171            numberFormat = NumberFormatInfo.GetInstance(new CultureInfo("de-DE"));
     172            dateTimeFormatInfo = DateTimeFormatInfo.GetInstance(new CultureInfo("de-DE"));
    146173            separator = POSSIBLE_SEPARATORS
    147174              .Except(disallowedSeparators)
     
    154181          // no points and no commas => English format
    155182          numberFormat = NumberFormatInfo.InvariantInfo;
     183          dateTimeFormatInfo = DateTimeFormatInfo.InvariantInfo;
    156184          separator = POSSIBLE_SEPARATORS
    157185            .Where(c => OccurrencesOf(charCounts, c) > 10)
     
    169197    #region tokenizer
    170198    internal enum TokenTypeEnum {
    171       NewLine, Separator, String, Double
     199      NewLine, Separator, String, Double, DateTime
    172200    }
    173201
     
    176204      public string stringValue;
    177205      public double doubleValue;
     206      public DateTime dateTimeValue;
    178207
    179208      public Token(TokenTypeEnum type, string value) {
    180209        this.type = type;
    181210        stringValue = value;
     211        dateTimeValue = DateTime.MinValue;
    182212        doubleValue = 0.0;
    183213      }
     
    193223      private List<Token> tokens;
    194224      private NumberFormatInfo numberFormatInfo;
     225      private DateTimeFormatInfo dateTimeFormatInfo;
    195226      private char separator;
    196227      private const string INTERNAL_SEPARATOR = "#";
     
    218249      }
    219250
    220       public Tokenizer(StreamReader reader, NumberFormatInfo numberFormatInfo, char separator) {
     251      public Tokenizer(StreamReader reader, NumberFormatInfo numberFormatInfo, DateTimeFormatInfo dateTimeFormatInfo, char separator) {
    221252        this.reader = reader;
    222253        this.numberFormatInfo = numberFormatInfo;
     254        this.dateTimeFormatInfo = dateTimeFormatInfo;
    223255        this.separator = separator;
    224256        separatorToken = new Token(TokenTypeEnum.Separator, INTERNAL_SEPARATOR);
     
    264296          token.type = TokenTypeEnum.Double;
    265297          return token;
    266         }
    267 
    268         // couldn't parse the token as an int or float number so return a string token
     298        } else if (DateTime.TryParse(strToken, out token.dateTimeValue)) {
     299          token.type = TokenTypeEnum.DateTime;
     300          return token;
     301        }
     302
     303        // couldn't parse the token as an int or float number  or datetime value so return a string token
    269304        return token;
    270305      }
     
    299334    private void ParseValues() {
    300335      while (tokenizer.HasNext()) {
    301         List<double> row = new List<double>();
    302         row.Add(NextValue(tokenizer));
     336        List<object> row = new List<object>();
     337        object value = NextValue(tokenizer);
     338        if (value == null) { tokenizer.Next(); continue; }
     339        row.Add(value);
    303340        while (tokenizer.HasNext() && tokenizer.Peek() == tokenizer.SeparatorToken) {
    304341          Expect(tokenizer.SeparatorToken);
     
    312349            "\nLine " + tokenizer.CurrentLineNumber + " has " + row.Count + " columns.", "", tokenizer.CurrentLineNumber);
    313350        }
    314         // add the current row to the collection of rows and start a new row
    315351        rowValues.Add(row);
    316         row = new List<double>();
    317       }
    318     }
    319 
    320     private double NextValue(Tokenizer tokenizer) {
    321       if (tokenizer.Peek() == tokenizer.SeparatorToken || tokenizer.Peek() == tokenizer.NewlineToken) return double.NaN;
     352        row = new List<object>();
     353      }
     354    }
     355
     356    private object NextValue(Tokenizer tokenizer) {
     357      if (tokenizer.Peek() == tokenizer.SeparatorToken) return string.Empty;
     358      if (tokenizer.Peek() == tokenizer.NewlineToken) return null;
    322359      Token current = tokenizer.Next();
    323       if (current.type == TokenTypeEnum.Separator || current.type == TokenTypeEnum.String) {
     360      if (current.type == TokenTypeEnum.Separator) {
    324361        return double.NaN;
     362      } else if (current.type == TokenTypeEnum.String) {
     363        return current.stringValue;
    325364      } else if (current.type == TokenTypeEnum.Double) {
    326         // just take the value
    327365        return current.doubleValue;
     366      } else if (current.type == TokenTypeEnum.DateTime) {
     367        return current.dateTimeValue;
    328368      }
    329369      // found an unexpected token => throw error
     
    334374
    335375    private void ParseVariableNames() {
    336       // if the first line doesn't start with a double value then we assume that the
    337       // first line contains variable names
    338       if (tokenizer.HasNext() && tokenizer.Peek().type != TokenTypeEnum.Double) {
    339 
    340         List<Token> tokens = new List<Token>();
    341         Token valueToken;
     376      //if first token is double no variables names are given
     377      if (tokenizer.Peek().type == TokenTypeEnum.Double) return;
     378
     379      // the first line must contain variable names
     380      List<Token> tokens = new List<Token>();
     381      Token valueToken;
     382      valueToken = tokenizer.Next();
     383      tokens.Add(valueToken);
     384      while (tokenizer.HasNext() && tokenizer.Peek() == tokenizer.SeparatorToken) {
     385        Expect(tokenizer.SeparatorToken);
    342386        valueToken = tokenizer.Next();
    343         tokens.Add(valueToken);
    344         while (tokenizer.HasNext() && tokenizer.Peek() == tokenizer.SeparatorToken) {
    345           Expect(tokenizer.SeparatorToken);
    346           valueToken = tokenizer.Next();
    347           if (valueToken != tokenizer.NewlineToken) {
    348             tokens.Add(valueToken);
    349           }
    350         }
    351387        if (valueToken != tokenizer.NewlineToken) {
    352           Expect(tokenizer.NewlineToken);
    353         }
    354         variableNames = tokens.Select(x => x.stringValue.Trim()).ToList();
    355       }
     388          tokens.Add(valueToken);
     389        }
     390      }
     391      if (valueToken != tokenizer.NewlineToken) {
     392        Expect(tokenizer.NewlineToken);
     393      }
     394      variableNames = tokens.Select(x => x.stringValue.Trim()).ToList();
    356395    }
    357396
Note: See TracChangeset for help on using the changeset viewer.