将时间和日期转换为相对时间(CSV 处理)
Converting Time & Date to relative time (CSV processing)
我目前正处于编写 multi-faceted 投资算法的早期阶段。我目前正在研究的部分涉及使用具有 LASSO 惩罚的图形高斯模型来找到可用于告知投资策略的 inter-dependencies。我目前正在尝试使用 JAVA 到 pre-process 历史 CSV 数据输入,并使用相关数据创建一个新的 CSV 输出文件。
我用来测试处理算法(最终将用于 Reuters Eikon 实时提要)的原始 small-scale 示例数据采用 txt/CSV 格式。我有一个文件夹,其中包含包含纽约证券交易所许多股票历史数据的文本文件。尽管有 8 列,但我感兴趣的三列(出于 pre-process 在创建将馈入 'GLASSO' 的协方差矩阵之前的目的)是日期、时间和开盘价。开盘价列不需要 pre-processing,因此可以将其输入到一个新的、噪音较小的输出文件中。
我的问题是如何将两列(日期和时间)转换为单个时间测量值。我当时认为最明显的方法是在我的数据中找到最早的时间点并将其用作点 0(以秒为单位)。然后,我需要将每个时间和日期组合转换为一个列,显示输出 CSV 文件中超过原始时间点的秒数。完成此操作而不是文件路径规范后,我希望能够指定一个文件夹,程序循环遍历所有文本文件以查找相关列并将所有内容输出到单个 CSV 文件中。
这在实践中会是什么样子:
CSV 标题和一个 NYSE txt 文件中的第一个条目 -
"日期、时间、开盘价、最高价、最低价、收盘价、交易量、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 秒)
刚到re-iterate,输入的是一个包含数百个以下格式的CSV的文件夹:
列 -
1:日期
2:时间
3:打开
4:高
5:低
6:关闭
7:音量
8: OpenInt
输出是一个 CSV 文件,包含:
列 -
1:时间测量(距离最早进入点的秒数)
2:每个时间点的股票价格。
如果您对我如何做这件事有任何线索,请告诉我,如果有什么我可以澄清的,请随时告诉我,让您的生活更轻松,我知道我可能有以一种不那么复杂的方式解释了这一点。
这是一个使用您提供的示例 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
java.time
Answer by Saviour Self 看起来是正确的。但它使用了旧的 date-time 类,这些 java.time 框架已被 Java 8 及更高版本中内置的 java.time 框架所取代。
Apache Commons CSV
作为奖励,我展示了如何使用 Apache Commons CSV 库来处理 reading/writing CSV 文件的繁琐工作。
首先我们通过制作一个StringReader
.
来模拟一个CSV文件
RFC 4180 规范
RFC 4180 规范正式定义了 CSV 格式。这方面的变化也存在。
RFC 4180 要求 Carriage Return + Line Feed (CRLF
) 作为换行符(行终止符).最后一行的终止符是可选的,我们将其包含在此处。
我们省略了可选的 header 行(列标题)。
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
objects to represent each row of incoming data. One line of code does all that work, with CSVFormat::parse
producing a CSVParser
object (an implementation of 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
objects. Remember the first one as our baseline, stored here as an Instant
的 collection(下面讨论)。然后循环比较每个连续的 CSVRecord
object,检查每个字段作为 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.
提取date-only和time-only的字符串,合并成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.
这个date-timeobject不是时间轴上的一个点,因为我们不知道它的offset-from-UTC 或时区。如果您要使用这些值来计算 LocalDateTime
object 秒之间的增量,您将假设通用的 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
we can extract the same moment in UTC (an Instant
). Generally the 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
与原始基线 date-time 进行比较。 Duration.between
方法就是这样做的。
Duration duration = Duration.between ( firstInstant , instant );
我们以总秒数计算增量。
Long deltaInSeconds = duration.getSeconds ();
将这些结果写入输出 CSV 文件留给 reader 作为练习。 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
我目前正处于编写 multi-faceted 投资算法的早期阶段。我目前正在研究的部分涉及使用具有 LASSO 惩罚的图形高斯模型来找到可用于告知投资策略的 inter-dependencies。我目前正在尝试使用 JAVA 到 pre-process 历史 CSV 数据输入,并使用相关数据创建一个新的 CSV 输出文件。
我用来测试处理算法(最终将用于 Reuters Eikon 实时提要)的原始 small-scale 示例数据采用 txt/CSV 格式。我有一个文件夹,其中包含包含纽约证券交易所许多股票历史数据的文本文件。尽管有 8 列,但我感兴趣的三列(出于 pre-process 在创建将馈入 'GLASSO' 的协方差矩阵之前的目的)是日期、时间和开盘价。开盘价列不需要 pre-processing,因此可以将其输入到一个新的、噪音较小的输出文件中。
我的问题是如何将两列(日期和时间)转换为单个时间测量值。我当时认为最明显的方法是在我的数据中找到最早的时间点并将其用作点 0(以秒为单位)。然后,我需要将每个时间和日期组合转换为一个列,显示输出 CSV 文件中超过原始时间点的秒数。完成此操作而不是文件路径规范后,我希望能够指定一个文件夹,程序循环遍历所有文本文件以查找相关列并将所有内容输出到单个 CSV 文件中。
这在实践中会是什么样子:
CSV 标题和一个 NYSE txt 文件中的第一个条目 -
"日期、时间、开盘价、最高价、最低价、收盘价、交易量、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 秒)
刚到re-iterate,输入的是一个包含数百个以下格式的CSV的文件夹:
列 - 1:日期 2:时间 3:打开 4:高 5:低 6:关闭 7:音量 8: OpenInt
输出是一个 CSV 文件,包含:
列 - 1:时间测量(距离最早进入点的秒数) 2:每个时间点的股票价格。
如果您对我如何做这件事有任何线索,请告诉我,如果有什么我可以澄清的,请随时告诉我,让您的生活更轻松,我知道我可能有以一种不那么复杂的方式解释了这一点。
这是一个使用您提供的示例 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
java.time
Answer by Saviour Self 看起来是正确的。但它使用了旧的 date-time 类,这些 java.time 框架已被 Java 8 及更高版本中内置的 java.time 框架所取代。
Apache Commons CSV
作为奖励,我展示了如何使用 Apache Commons CSV 库来处理 reading/writing CSV 文件的繁琐工作。
首先我们通过制作一个StringReader
.
RFC 4180 规范
RFC 4180 规范正式定义了 CSV 格式。这方面的变化也存在。
RFC 4180 要求 Carriage Return + Line Feed (CRLF
) 作为换行符(行终止符).最后一行的终止符是可选的,我们将其包含在此处。
我们省略了可选的 header 行(列标题)。
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
objects to represent each row of incoming data. One line of code does all that work, with CSVFormat::parse
producing a CSVParser
object (an implementation of 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
objects. Remember the first one as our baseline, stored here as an Instant
的 collection(下面讨论)。然后循环比较每个连续的 CSVRecord
object,检查每个字段作为 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.
提取date-only和time-only的字符串,合并成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.
这个date-timeobject不是时间轴上的一个点,因为我们不知道它的offset-from-UTC 或时区。如果您要使用这些值来计算 LocalDateTime
object 秒之间的增量,您将假设通用的 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
we can extract the same moment in UTC (an Instant
). Generally the 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
与原始基线 date-time 进行比较。 Duration.between
方法就是这样做的。
Duration duration = Duration.between ( firstInstant , instant );
我们以总秒数计算增量。
Long deltaInSeconds = duration.getSeconds ();
将这些结果写入输出 CSV 文件留给 reader 作为练习。 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