我有一个或多或少“半结构化”的csv文件
rowNumber;ColumnA;ColumnB;ColumnC;
1;START; b; c;
2;;;;
4;;;;
6;END;;;
7;START;q;x;
10;;;;
11;END;;;
现在我想获取此行的数据 - > 1; START; b; C;填充,直到它在columnA中找到'END'。那么它应该采取这一行 - > 7; START; Q; X;并使用值填充下面的单元格,直到下一个'END'(此处:11; END ;;;)
我是一个完全的初学者,对我来说非常艰难,我应该如何开始:
import au.com.bytecode.opencsv.CSVReader
import java.io.FileReader
import scala.collection.JavaConversions._
import scala.collection.mutable.ListBuffer
val masterList = new CSVReader(new FileReader("/root/csvfile.csv"),';')
var startList = new ListBuffer[String]()
var derivedList = new ListBuffer[String]()
for (row <- masterList.readAll) {
row(1) = row(1).trim
if (row(1) == "START")
startList += row(0)
}
var i = 0
for (i <- i to startList.length ) {
for(row <- masterList.readAll)
if (row(0) > startList(i) && row(0) < startList(i+1)) {
derivedList += row
}
}
我开始使用CSVReader读取文件并创建一个masterList。我创建了一个循环并迭代并将所有START值放入其中(因此我知道从START到下一个START的范围)。 我创建了第二个循环,我想将数据集放入一个新的ListBuffer中。但这不起作用
下一步是合并masterList +派生List。
我需要一些好的想法,或者朝着正确的方向努力,我该如何进行,或者我如何能够更容易地做到这一点? 非常感谢帮助!!
我不知道,如果首先存在很大差异,但我想创建一个Apache Spark应用程序。还可以选择在Python中执行此操作(如果更容易)
输出应如下所示: 它应该看起来像
1;START; b; c;
2;;b;c;
4;;b;c;
6;END;;;
7;START;q;x;
10;;q;x;
11;END;;;
你永远不会用END触及该行。只需用ColumnB和ColumnC填充START下面的行
答案 0 :(得分:0)
显然你误用了“readAll”函数用于流。
for (i <- i to startList.length ) {
for(row <- masterList.readAll)
if (row(0) > startList(i) && row(0) < startList(i+1)) {
derivedList += row
}
}
在第一个readAll函数之后,第二个函数无法读取任何内容,因为第一个readAll将流指针指向文件末尾,需要重置strampointer。
答案 1 :(得分:0)
我推荐已经给出的问题/答案的解决方案(使用库),这里有一个如何不做的答案:P
val text = """rowNumber;ColumnA;ColumnB;ColumnC;
1;START; b; c;
2;;;;
4;;;;
6;END;;;
7;START;q;x;
10;;;;
11;END;;;"""
val groupsRe = """(?s)\S*?START.*?END\S*""".r
val valuesRe = """^.*?START;\s*(\S*);\s*(\S*);""".r
groupsRe.findAllIn(text)
.map(_.split("\n"))
.map({ lines =>
lines.head match {
case valuesRe(a, b) =>
lines.map(_.replaceAll(";;;;", s";;$a;$b;"))
case g =>
println(s"Don't recognise $g")
lines
}
}).map(_.mkString("\n"))
.mkString("\n")
res0: String = 1;START; b; c;
2;;b;c;
4;;b;c;
6;END;;;
7;START;q;x;
10;;q;x;
11;END;;;
答案 2 :(得分:0)
使用scanleft [1]的功能很少。从readAll中删除(1)因为它也读取了标题。同时在最后放下(1)因为scanleft以开始结果开始。
ScanLeft类型允许您使用先前的计算值,它采用两个参数的函数,首先是前一个计算的结果,第二个参数是列表的当前值(迭代器..)。它需要列表的第一个元素的标记类型的值,在这种情况下我提供空列表。
现在我正在传递给scanleft的函数(prev,curr)=&gt; .... 如果作为csv的当前行的curr以&#34; START&#34;开头。或者&#34;结束&#34;,不需要做任何有预期价值的事情。其他明智的(case _)我们需要获取当前行的前两个值并追加除前两列之外的前一行(即drop 2)。你也可以附上prev(2)和prev(3)。
最后有一个下降(1),因为扫描左边返回带有哨兵的第一个,即我们不需要的起始值。
val reader = new CSVReader(new FileReader("test.in"),';')
val start = List("","","","")
val res = reader.readAll.drop(1).scanLeft(start)((prev,curr) => {
curr(1) match {
case "START" => curr.toList
case "END" => curr.toList
case _ => curr(0) :: curr(1) :: (prev drop 2)
}
}).drop(1)
查看结果
for(r <- res) {
println(r)
}
这将输出
List(1, START, b, c, )
List(2, , b, c, )
List(4, , b, c, )
List(6, END, , , )
List(7, START, q, x, )
List(10, , q, x, )
List(11, END, , , )
答案 3 :(得分:0)
如果您想要一个简单的现有解决方案,请尝试使用Spark-csv https://github.com/databricks/spark-csv
它会将文件加载为DataFrame,因此处理数据的情况会更好。
使用以下命令启动spark-shell
$ bin/spark-shell --packages com.databricks:spark-csv_2.10:1.0.3
val doc1 = sqlContext.read.format("com.databricks.spark.csv").option("delimiter","\t").option("header","true").load("/Users/abc/Documents/doc1.csv")
内容看起来像 -
+-----+------+----+------+----+
| NOX| RM| AGE| DIS|CHAS|
+-----+------+----+------+----+
|0.538| 6.575|65.2| 4.09| 1|
|0.469| 6.421|78.9|4.9671| 2|
|0.469| 7.185|61.1|4.9671| 3|
|0.458| 6.998|45.8|6.0622| 4|
|0.458| 7.147|54.2|6.0622| 5|
|0.458| 6.43|58.7|6.0622| 6|
|0.524| 6.012|66.6|5.5605| 7|
|0.524| 6.172|96.1|5.9505| 1|
|0.524| 5.631| 100|6.0821| 2|
|0.524| 6.004|85.9|6.5921| 3|
|0.524| 6.377|94.3|6.3467| 4|
|0.524| 6.009|82.9|6.2267| 5|
|0.524| 7.009| 39|5.4509| 6|
|0.538| 8.009|61.8|4.7075| 7|
|0.538| 9.009|84.5|4.4619| 8|
|0.538|10.009|56.5|4.4986| 9|
|0.538|11.009|29.3|4.4986| 10|
|0.538|12.009|81.7|4.2579| 11|
|0.538|13.009|36.6|3.7965| 12|
|0.538|14.009|69.5|3.7965| 6|
+-----+------+----+------+----+
现在,您可以使用Spark-Sql或数据框操作(如过滤)来根据需要预处理数据。
答案 4 :(得分:0)
你可以使用MasterDetailProcessor中内置的uniVocity-parsers - (spark-csv使用uniVocity-parsers,不确定他们是否通过spark使RowProcessor实现容易获得CSV)。
如果您自己使用库,可以使用以下代码解决问题(对不起,请使用Java):
CsvParserSettings settings = new CsvParserSettings(); //many more options here, check the documentation.
settings.getFormat().setDelimiter(';');
settings.getFormat().setLineSeparator("\n");
settings.setHeaderExtractionEnabled(true); // extract headers from first row
//A processor of master detail records. The master row is at the at top, so you have RowPlacement.TOP here.
//The second arg is an instance of ObjectRowListProcessor - this allows you to configure data conversions - check the docs
MasterDetailListProcessor processor = new MasterDetailListProcessor(RowPlacement.TOP, new ObjectRowListProcessor()) {
@Override
protected boolean isMasterRecord(String[] row, ParsingContext context) {
return row.length > 1 && "START".equals(row[1]);
}
};
//Configures the parser to to your master detail processor.
settings.setRowProcessor(processor);
//The parse method will process the input and submit the rows to your processor
new CsvParser(settings).parse(new FileReader(new File("path/to/your.csv")));
//All you need to do is to get the (master) records here:
List<MasterDetailRecord> masterRecords = processor.getRecords();
for(MasterDetailRecord master : masterRecords){
System.out.println("Master row:" + Arrays.toString(master.getMasterRow()));
// Each master record has detail rows (everything after your "START" row)
for(Object[] childRow : master.getDetailRows()){
//By default you will get NULLS instead of blank Strings - probably easier for you to work with
//This can be configured in the parser settings if you need.
System.out.println("\t" + Arrays.toString(childRow));
}
}
输出:
Master row:[1, START, b, c, null]
[2, null, null, null, null]
[4, null, null, null, null]
[6, END, null, null, null]
Master row:[7, START, q, x, null]
[10, null, null, null, null]
[11, END, null, null, null]
披露:我是这个图书馆的作者。它是开源和免费的(Apache V2.0许可证)。