用Scala填充csv

时间:2015-06-21 13:16:19

标签: python scala csv apache-spark

我有一个或多或少“半结构化”的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下面的行

5 个答案:

答案 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, , , )

[1]。 Reduce, fold or scan (Left/Right)?

答案 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许可证)。