Translate

пятница, 5 декабря 2014 г.

Создаём парсер

Поскольку для алгоритма машинного обучения необходимы данные, то эти данные нужно откуда-то загружать. Чаще всего для хранения и передачи данных используется наиболее распространенный формат файлов CSV. Данный формат был разработан вовсе не для машинного обучения, а для обмена данными между различными электронными таблицами. Но пригодился и в искусственном интеллекте, поскольку обучающие выборки также являются табличными данными. Более того, зачастую информацию, например, в научных исследованиях, сначала заносят в электронные таблицы, а потом уже с помощью машинного обучения пытаются найти закономерности.


Поэтому, наш парсер также будет брать данные из файлов в формате CSV.  В качестве разделителей табличных ячеек будет использоваться символ точки с запятой. А в качестве разделителя дробной и целой части числовых данных будет использоваться либо точка, либо запятая. К тому же первая строка файла будет содержать наименования входных данных (зависимых переменных). Вторая строка будет содержать наименования единиц измерений для данных. Начиная с третьей строки пойдут уже сами данные в виде чисел. Первый столбец файла будет содержать идентификаторы строк. Зависимая переменная у нас будет всего одна и не будет иметь наименований, а соответственно и единиц измерений. Диапазон зависимой переменной такой же как и для логистической регрессии: 1 - событие истинно, 0 - событие ложно.
Конечно же, необходимо не только парсить информацию из файлов, но и обрабатывать ошибки. В нашем случае недопустимо, если какие либо числовые данные будут представлены в нечисловом формате. Также недопустимы пропущенные данные, для этого будет применяться проверка на количество ячеек в строках и если будут найдены различия, то должно выдаваться сообщение об ошибке.

В таком случае, если например, нам необходимо будет создать файл для машинного обучения задаче XOR, то содержимое файла, созданное в текстовом редакторе будет выглядеть так:

;v0;v1;
;n/a;n/a;
sample0;0;0;0
sample1;0;1;1
sample2;1;0;1
sample3;1;1;0

Сохраним файл с именем xor.csv

Теперь его можно открыть в какой нибудь электронной таблице, например, в MS Excel или в Open Office Calc.


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

Создадим код парсера на Java:


package libvmr;

import java.io.*;
import java.util.*;

/**
 * Парсинг входного файла из формата CSV
 * @author Yury V. Reshetov
 * @version 1.0
 */
public class Parser {
    
    /**
     * Названия входных признаков
     * 
     */
    private String[] ids = null;

    /**
     * Единицы измерения для входных признаков
     */
    private String[] units = null;
    
    /**
     * Сообщение об ошибке
     */
    private String error = null;

    /**
     * Парсинг
     * @param file Имя входного файла
     * @return Массив обучающей выборки
     */
    public double[][] parsing(File file) {
        try {
            // Загружаем весь текст в ОЗУ
            BufferedReader bufferedreader = new BufferedReader(new FileReader(file));
            String s = "";
            String strings = "";
            while ((s = bufferedreader.readLine()) != null) {
                if (! s.trim().equals("")) {
                    strings = strings + s + "\n";
                }
            }
            bufferedreader.close();
            //Считаем количество строк и столбцов
            StringTokenizer stringtokenizer = new StringTokenizer(strings, "\n");
            int i = 0; // Счетчик количества строк
            int j = 0; // Счетчик количества столбцов
            while (stringtokenizer.hasMoreTokens()) {
                String string = stringtokenizer.nextToken().trim();
                StringTokenizer stringtokenizer1 = new StringTokenizer(string, ";");
                j = 0;
                while (stringtokenizer1.hasMoreTokens()) {
                    stringtokenizer1.nextToken();
                    j++;
                }
                i++;
            }
            int rows = i - 2;      // Количество строк в обучающей выборке
            int colls = j - 1; // Количество столбцов в обучающей выборке
            double[][] samples = new double[rows][colls];
            this.ids = new String[colls - 1];
            this.units = new String[colls - 1];

            // Первые две строки: наименования входных значений и их единицы измерения
            stringtokenizer = new StringTokenizer(strings, "\n");


            if (i > 2) {
                for (int n = 0; n < 2; n++) {
                    String string = stringtokenizer.nextToken().trim();
                    StringTokenizer stringtokenizer1 = new StringTokenizer(string, ";");
                    if (! string.startsWith(";")) {
                    // Пропускаем первый столбец
                        if (stringtokenizer1.hasMoreTokens()) {
                            stringtokenizer1.nextToken();
                        }
                    }
                    j = 0;
                    while (stringtokenizer1.hasMoreTokens()) {
                        String data = stringtokenizer1.nextToken();
                        if (n == 0) {
                            this.ids[j] = data.trim();
                        } else {
                            this.units[j] = data.trim();
                        }
                        j++;
                    }
                }
            }
            // Строки, начиная с третьей
            i = 0;
            int prev_j = 0; // Количество элементов в предыдущей строке
            while (stringtokenizer.hasMoreTokens()) {
                String string = stringtokenizer.nextToken().trim();
                StringTokenizer stringtokenizer1 = new StringTokenizer(string, ";");
                prev_j = j;
                j = 0;
                // Пропускаем первый столбец
                if (! string.startsWith(";")) {
                    if (stringtokenizer1.hasMoreTokens()) {
                        stringtokenizer1.nextToken();
                    }
                }
                while (stringtokenizer1.hasMoreTokens()) {
                    String data = stringtokenizer1.nextToken();
                    // Заменим все разделители-запятые для числовых строк на точки
                    samples[i][j] = Double.parseDouble(data.replace(',','.'));
                    j++;
                }
                if ((i > 0) && (j != prev_j)) {
                    int current = i + 1;
                    if (j > prev_j) {
                        this.error = "Количество элементов в строке " + i + " меньше чем в строке " + current;
                    } 
                    if ((j < prev_j)) {
                        this.error = "Количество элементов в строке " + current + " меньше чем в строке " + i;
                    }
                    return null;
                }
                i++;
            }
            return samples;
        } catch (Exception exception) {
            this.error = exception.getMessage();
        }
        return null;
    }

    /**
     * Метод возвращает названия входных параметров
     * @return названия входных параметров
     */
    public String[] getIDs() {
        return this.ids;
    }

    /**
     * Метод возвращает наименования единиц измерения
     * @return наименования единиц измерения
     */
    public String[] getUnits() {
        return this.units;
    }


    /**
     * Метод возвращает сообщение об ошибке
     * @return сообщение об ошибке, либо null если ошибки отсутствуют
     */
    public String getError() {
        return this.error;
    }
}



Комментариев нет:

Отправить комментарий