Преобразование времени и даты в относительное время (обработка CSV)

В настоящее время я нахожусь на ранних стадиях написания многогранного инвестиционного алгоритма. Часть, над которой я сейчас работаю, связана с использованием графической гауссовой модели со штрафом LASSO для нахождения взаимозависимостей, которые можно использовать для обоснования инвестиционной стратегии. В настоящее время я пытаюсь использовать JAVA для предварительной обработки ввода исторических данных CSV и создания нового выходного файла CSV с соответствующими данными.

Необработанные мелкомасштабные примеры данных, которые я использую для проверки алгоритма обработки (который в конечном итоге будет использоваться в прямом эфире Reuters Eikon), представлены в формате txt/CSV. У меня есть папка, содержащая текстовые файлы с историческими данными о многих акциях на NYSE. Хотя существует 8 столбцов, три из них, которые меня интересуют (в целях предварительной обработки перед созданием ковариационной матрицы, которая будет подана в "GLASSO"), представляют собой цены Дата, Время и Открытие. Столбец цен открытия не требует предварительной обработки, поэтому его можно вставить в новый, менее шумный выходной файл.

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

Как это будет выглядеть на практике?

Название CSV и первая запись в одном текстовом файле NYSE -

"Дата, время, Open, High, Low, Close, Volume, OpenInt

2016-02-03,15: 35: 00,37.27,37.36,37.17,37.29,25274,0"

По сути, если первая запись является самой ранней временной ссылкой:

2016-02-03,15: 35: 00 = '0'

2016-02-03, 15: 40: 00 = "300" (5 минут - 300 секунд)

Просто для повторения, входные данные - это папка, содержащая сотни следующих форматированных CSV:

Столбцы - 1: Дата 2: Время 3: Открыть 4: Высокий 5: Низкий 6: Закрыть 7: Том 8: OpenInt

Вывод представляет собой один файл CSV, содержащий:

Столбцы - 1: Мера времени (расстояние в секундах от самой ранней точки входа) 2: Цена акций для каждого входа в меру времени.

Пожалуйста, дайте мне знать, если у вас есть какие-либо подсказки о том, как я могу это сделать, не стесняйтесь, дайте мне знать, если есть что-то, что я могу уточнить, чтобы облегчить вашу жизнь, я понимаю, что мог бы объяснить это в меньшей степени запутанная манера.

2 ответа

Решение

java.time

Ответ Спасителя Сам выглядит правильно. Но он использует старые классы даты и времени, которые были вытеснены инфраструктурой java.time, встроенной в Java 8 и более поздние версии.

Apache Commons CSV

В качестве бонуса я покажу, как использовать библиотеку Apache Commons CSV для выполнения рутинной работы по чтению / записи CSV-файлов.

Сначала мы моделируем файл CSV, делая StringReader,

RFC 4180 спецификация

Спецификация RFC 4180 формально определяет формат CSV. Вариации на это тоже существуют.

RFC 4180 требует возврата каретки + перевод строки (CRLF) как перевод строки (терминатор строки). Терминатор последней строки является необязательным, который мы включаем здесь.

Мы опускаем необязательную строку заголовка (заголовки столбцов).

String newline = "\r\n";
StringBuilder input = new StringBuilder ();
input.append ( "2016-02-03,15:10:00,37" ).append ( newline );
input.append ( "2016-02-03,15:15:00,38" ).append ( newline );  // 5 minutes later.
input.append ( "2016-02-03,15:17:00,39" ).append ( newline );  // 2 minutes later.

Reader in = new StringReader ( input.toString () );

Далее мы читаем весь файл CSV в память, где создается библиотека Commons CSV CSVRecord объекты для представления каждой строки входящих данных. Одна строка кода делает всю эту работу, с CSVFormat::parse производя CSVParser объект (реализация Interable).

Iterable<CSVRecord> records;
try {
    records = CSVFormat.DEFAULT.parse ( in );  // 'records' is a CSVParser.
} catch ( IOException ex ) {
    // FIXME: Handle exception.
    System.out.println ( "[ERROR] " + ex );
    return; // Bail-out.
}

Теперь мы анализируем эту коллекцию CSVRecord объекты. Помните первый как наш базовый уровень, хранящийся здесь как Instant (обсуждается ниже). Затем цикл, чтобы сравнить каждый последующий CSVRecord объект, исследуя каждое поле как String,

Instant firstInstant = null; // Track the baseline against which we calculate the increasing time
for ( CSVRecord record : records ) {
    String dateInput = record.get ( 0 );  // Zero-based index.
    String timeInput = record.get ( 1 );
    String priceInput = record.get ( 2 );
    //System.out.println ( dateInput + " | " + timeInput + " | " + priceInput );  // Dump input strings for debugging.

Извлеките строки только для даты и времени, объедините в LocalDateTime,

    // Parse strings.
    LocalDate date = LocalDate.parse ( dateInput );
    LocalTime time = LocalTime.parse ( timeInput );
    Integer price = Integer.parseInt ( priceInput );
    // Combine date and time.
    LocalDateTime ldt = LocalDateTime.of ( date , time );  // Not a specific moment on the timeline.

Этот объект даты и времени не является точкой на временной шкале, так как мы не знаем его смещения от UTC или часового пояса. Если бы вы использовали эти значения для вычисления дельты между LocalDateTime объекты, вы предполагаете, что обычные 24-часовые дни свободны от аномалий, таких как переход на летнее время (DST). Вам может это сойти с рук, если ваши данные случайно не появятся во время каких-либо аномалий, но это плохая привычка. Лучше назначить часовой пояс, если известен.

Мы знаем источник данных, поэтому мы можем предположить, что предполагаемый часовой пояс, ZoneId, Назначая этот предполагаемый часовой пояс, мы получаем реальный момент на временной шкале.

    // Generally best to assign the time zone known to apply to this incoming data.
    ZoneId zoneId = ZoneId.of ( "America/New_York" );  // Move this line somewhere else to eliminate needless repetition.
    ZonedDateTime zdt = ldt.atZone ( zoneId );  // Now this becomes a specific moment on the timeline.

От этого ZonedDateTime мы можем извлечь тот же момент в UTC (Instant). Как правило, Instant это то, что вы должны использовать для хранения данных, обмена данными, сериализации и так далее. Вам нужно только ZonedDateTime для представления пользователю в его ожидаемом часовом поясе.

    Instant instant = zdt.toInstant ();  // Use Instant (moment on the timeline in UTC) for data storage, exchange, serialization, database, etc.
    if ( null == firstInstant ) {
        firstInstant = instant;  // Capture the first instant.
    }

Цель состоит в том, чтобы сравнить каждый CSVRecord к исходной базовой дате и времени. Duration.between метод делает именно это.

    Duration duration = Duration.between ( firstInstant , instant );

Мы вычисляем дельту в общем количестве секунд.

    Long deltaInSeconds = duration.getSeconds ();

Запись этих результатов в выходной CSV-файл оставлена ​​для читателя в качестве упражнения. Библиотека Apache Commons CSV быстро справляется с задачами: пишет и читает форматы CSV.

    // … output the deltaInSeconds & price to CSV. Apache Commons CSV can write as well as read CSV files.
    System.out.println ( "deltaInSeconds: " + deltaInSeconds + " | price: " + price );

}

Когда беги.

deltaInSeconds: 0 | price: 37
deltaInSeconds: 300 | price: 38
deltaInSeconds: 420 | price: 39

Вот пример с использованием приведенной вами строки CSV. Я изменил ввод, чтобы изменить секунды, чтобы вы могли увидеть, как работает разница:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Dater {

    String s1[] = {"2016-02-03,15:35:01,37.27,37.36,37.17,37.29,25274,0",  //1 sec after minDate
                    "2016-02-03,15:35:00,37.27,37.36,37.17,37.29,25274,0", //<-- minDate
                    "2016-02-03,15:35:02,37.27,37.36,37.17,37.29,25274,0"  //2 sec after minDate
                    };
    Date [] dates;
    Date minDate;

    public Dater()
    {
        minDate = new Date();
        makeDates();

        for (Date d : dates)
        {
            System.out.println(diffSeconds(d));
        }
    }
    public void makeDates()
    {
        dates = new Date[s1.length];
        int index = 0;
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (String s : s1)
        {
            String [] split = s.split(",");
            String date = split[0];
            String time = split[1];

            try {
                dates[index] = formatter.parse(date + " " + time); //make Date objects
                if (dates[index].compareTo(minDate) < 0)           //establish origin
                {
                    minDate = dates[index];
                }
            } catch (ParseException e)
            {
                e.printStackTrace();
            }
            index++;
        }
    }

    public Long diffSeconds(Date d)
    {
        return (d.getTime() - minDate.getTime()) / 1000;
    }

    public static void main(String...args)
    {
        new Dater();
    }
}

Выход:

1
0
2
Другие вопросы по тегам