【问题标题】:Converting Time & Date to relative time (CSV processing)将时间和日期转换为相对时间(CSV 处理)
【发布时间】:2016-02-22 17:21:31
【问题描述】:

我目前正处于编写多方面投资算法的早期阶段。我目前正在研究的部分是关于使用带有 LASSO 惩罚的图形高斯模型来找到可用于为投资策略提供信息的相互依赖关系。我目前正在尝试使用 JAVA 对历史 CSV 数据输入进行预处理,并使用相关数据创建一个新的 CSV 输出文件。

我用来测试处理算法(最终将用于 Reuters Eikon 实时提要)的原始小规模示例数据采用 txt/CSV 格式。我有一个文件夹,其中包含文本文件,其中包含纽约证券交易所许多股票的历史数据。虽然有 8 列,但我感兴趣的三列(为了在创建将输入“GLASSO”的协方差矩阵之前进行预处理)是日期、时间和开盘价。开盘价列不需要预处理,因此可以输入到新的、噪音较小的输出文件中。

我的问题是如何将两列(日期和时间)转换为单个时间度量。我在想最明显的方法是在我的数据中找到最早的时间点并将其用作点 0(以秒为单位)。然后,我需要将每个时间和日期组合转换为单个列,显示它在输出 CSV 文件中的原始时间点之后的秒数。一旦完成而不是指定文件路径规范,我希望能够指定一个文件夹,程序循环遍历所有文本文件,找到相关列并将所有内容输出到单个 CSV 文件中。

这在实践中会是什么样子:

一个 NYSE txt 文件中的 CSV 标题和第一个条目 -

"日期、时间、开盘、高盘、低盘、收盘、成交量、开盘时间

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:每次测量进入的股票价格。

如果您对我该如何做这件事有任何线索,请告诉我,如果有什么我可以澄清以使您的生活更轻松的事情,请随时告诉我,我知道我可以解释一下以一种不那么复杂的方式。

【问题讨论】:

  • 你能用最新的日期(甚至现在),然后据此计算吗?这样你的数字会说“x 小时前”或“x 天前”或“x 周前”?还是您希望此应用程序在其作为起始点的最早日期开始?
  • 您好 SS,非常感谢您的回复!这种预处理的结果是一个 CSV,它可以被读取并用于形成两个数组,而这两个数组又可以用来形成一个协方差矩阵,用于输入 GLASSO。它需要采用单个测量单位,因为它基本上都在同一轴上。因此,从找到的最早时间条目开始以秒为单位的时间是理想的。
  • 我的第一个想法是使用SimpleDateFormat 为每个条目构建一个Date 对象,并建立一个最小条目,以便您可以定义您的来源。获得原点后,您可以Compare the Date objects 返回原点与当前元素之间的秒数。这样你就可以在几秒钟内获得你的原点和距离。
  • 听起来很有希望,我会调查并报告结果,谢谢!
  • @lel23 Waaaaaaay 上面的信息太多了。当您直接进入 programming 问题时,StackOverflow 效果最佳。修改你的问题陈述以删除不相关的细节。尽可能简化您的数据示例以证明您的问题。

标签: java date csv time stock


【解决方案1】:

这是使用您提供的示例 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

【讨论】:

    【解决方案2】:

    java.time

    Answer by Saviour Self 看起来是正确的。但它使用的旧日期时间类已被 Java 8 及更高版本中内置的 java.time 框架所取代。

    Apache Commons CSV

    作为奖励,我将展示如何使用 Apache Commons CSV 库来处理读取/写入 CSV 文件的琐事。

    首先我们通过创建StringReader来模拟一个CSV文件。

    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.
    

    这个日期时间对象不是时间线上的一个点,因为我们不知道它的offset-from-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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多