首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Java中用组聚合CSV数据?

如何在Java中用组聚合CSV数据?
EN

Stack Overflow用户
提问于 2014-11-27 09:23:09
回答 5查看 4.3K关注 0票数 0

假设我有下面的应用程序的CSV文件操作日志。这个csv可能包含3-4百万行。

代码语言:javascript
运行
复制
Company, ActionsType, Action
ABC, Downloaded, Tutorial 1
ABC, Watched, Tutorial 2
PQR, Subscribed, Tutorial 1
ABC, Watched, Tutorial 2
PQR, Subscribed, Tutorial 3
XYZ, Subscribed, Tutorial 1
XYZ, Watched, Tutorial 3
PQR, Downloaded, Tutorial 1

是否有方法通过按公司名称分组并将actionType计数器显示为列来聚合这些数据,如下面使用Java所示?

代码语言:javascript
运行
复制
Company, Downloaded, Watched, Subscribed
ABC, 1, 2, 0
PQR, 1, 0, 2
XYZ, 0, 1, 1

我曾想过使用OpenCSV将CSV文件加载到列表中,但对于包含数百万数据的csv文件是否有效?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2014-11-27 10:46:33

如果你试图聚合数据,那肯定是没有效率的。您应该查看聚合大型数据的MapReduce。

下面是w/o MapReduce的解决方案:

代码语言:javascript
运行
复制
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.HashMap;

public class CSVMapper {

    public String transformCsv (String csvFile) {
        return csvMapToString(getCsvMap(csvFile));
    }

    private HashMap<String, Integer[]> getCsvMap (String csvFile) {
        // <K,V> := <Company, [Downloaded, Watched, Subscribed]>
        HashMap<String, Integer[]> csvMap = new HashMap<String, Integer[]>();
        BufferedReader reader = new BufferedReader(new StringReader(csvFile));
        String csvLine;

        // Create map
        try {
            while ((csvLine = reader.readLine()) != null) {
                String[] csvColumns = csvLine.split(",");
                if (csvColumns.length > 0) { 
                    try {
                        String company = csvColumns[0].trim();
                        String actionsType = csvColumns[1].trim();
                        Integer[] columnValues = csvMap.get(company);

                        if (columnValues == null) {
                            columnValues = new Integer[3];
                            columnValues[0] = columnValues[1] = columnValues[2] = 0;
                        }
                        columnValues[0] = columnValues[0] + (actionsType.equals("Downloaded") ? 1 : 0);
                        columnValues[1] = columnValues[1] + (actionsType.equals("Watched")    ? 1 : 0);
                        columnValues[2] = columnValues[2] + (actionsType.equals("Subscribed") ? 1 : 0);

                        if (!company.equals("Company"))
                            csvMap.put(company, columnValues);
                    }
                    catch (Exception nfe) {
                        //TODO: handle NumberFormatException
                    }
                }
            }
        }
        catch (Exception e) {
            //TODO: handle IOException
        }
        return csvMap;
    }

    private String csvMapToString (HashMap<String, Integer[]> csvMap) {
        StringBuilder newCsvFile = new StringBuilder(); 
        newCsvFile.append("Company, Downloaded, Watched, Subscribed\n");
        for (String company : csvMap.keySet()) {
            Integer[] columnValues = csvMap.get(company); 
            newCsvFile.append(company + 
                              ", " + Integer.toString(columnValues[0]) +
                              ", " + Integer.toString(columnValues[1]) +
                              ", " + Integer.toString(columnValues[2]) + "\n");
        }
        return newCsvFile.toString();
    }

    public static void main (String[] args) {
        String csvFile = "Company, ActionsType, Action\n" +
                     "ABC, Downloaded, Tutorial 1\n" +
                     "ABC, Watched, Tutorial 2\n" +
                     "PQR, Subscribed, Tutorial 1\n" +
                     "ABC, Watched, Tutorial 2\n" +
                     "PQR, Subscribed, Tutorial 3\n" +
                     "XYZ, Subscribed, Tutorial 1\n" +
                     "XYZ, Watched, Tutorial 3\n" +
                     "PQR, Downloaded, Tutorial 1";

        System.out.println( (new CSVMapper()).transformCsv(csvFile) );
    }
}
票数 2
EN

Stack Overflow用户

发布于 2014-11-27 10:05:32

由于您正在处理CSV中的数百万个条目,我不认为使用Java解析上述文件是这里最好的操作方式。

相反,我将使用Java.NET创建另一个应用程序,该应用程序将执行以下操作:

如果您有Oracle数据库:

a)使用sqlldr将CSV中的所有数据加载到数据库中。它将通过调用有关sqlldr:http://www.thegeekstuff.com/2012/06/oracle-sqlldr/sqlldr .More信息的外部进程调用来实现这一点。

( b)加载完成后,程序将运行一个查询来提取您需要的数据,如下所示:

代码语言:javascript
运行
复制
WITH T AS (SELECT COMPANY, ACTIONSTYPE FROM tmp_csv)
SELECT *
  FROM T PIVOT (COUNT (1)
         FOR ACTIONSTYPE
         IN ('Downloaded', 'Watched', 'Subscribed'))

( c)使用查询的ResultSet执行您想做的事情,将其保存到另一个csv或使用它创建一个新的表,以便您可以从那里查询结果(通过在查询之前用create table as语句对查询进行修改,可以很容易地实现后者)

如果您有SqlServer数据库:

进程与Oracle相同,但使用bcp实用程序从初始csv加载数据。更多信息在这里:http://msdn.microsoft.com/en-us/library/ms162802.aspx

现在,您可以在每次重新生成日志csv时运行此应用程序,并将持久数据保存在数据库中,您可以随意查询这些数据,生成报告等等,而且由于您将处理推送到数据库,所以它比纯java解决方案更有效。

如果坚持使用java解决方案,我建议使用并行性读取和处理来自csv的每条记录,添加结果并将输出写入一个新的csv文件中。

票数 1
EN

Stack Overflow用户

发布于 2014-11-27 10:19:31

例如,如果可以将CSV条目存储到MongoDB (首先将行转换为JSon ),则可以使用map/JSon数据处理。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27167026

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档