【问题标题】:Loop through a result set to generate averages by group循环遍历结果集以按组生成平均值
【发布时间】:2016-05-26 13:55:47
【问题描述】:

背景: 我正在尝试使用 Apache Commons Math 确定救护车的统计数据。我能够为一辆救护车做非常基本的单变量统计,但是当我想确定我车队中所有救护车的统计数据时,我会遇到困难。

目标: 我的目标是使用 JDBC 生成基本结果集,然后将信息解析为统计信息。例如,我想把结果集做成一个表格,显示救护车,2014 年的平均值,2015 年的平均值作为标题。 表格详细信息将显示每辆救护车和每个标题的平均值

<table>
<tr><th>ambulance</th><th>average response time for year 2014</th><th>average response time for year 2015</th></tr>
<tr><td>Medic1</td><td>62</td><td>74</td></tr>
<tr><td>Medic2</td><td>83</td><td>79</td></tr>
<tr><td>Medic3</td><td>68</td><td>71</td></tr>
</table>

尝试的伪代码: 伪代码看起来像这样; 1.) 为 2014 日历年的平均响应时间分配一个变量。 2.) 如果日历年是 2014 年,则遍历结果集中的所有救护车,然后计算平均值。 3.) 为 2015 日历年的平均响应时间分配一个变量。 4.)遍历所有救护车,如果日历年是 2015 年,则计算平均值。 5.) 输出救护车,2014年平均响应时间,2015年平均响应时间

评论: 这将是一个好的开始。至少会出现逻辑和格式以进行更复杂的分析,例如确定逐年的差异。但我被困住了。我不确定如何对每辆救护车进行迭代以生成平均值。

我能够编写 SQL 查询来生成每辆救护车的平均值。但我想使用 Apache Commons Math,因为它提供了 Skew、Kurtosis 和其他度量。您在本段上方看到的是更复杂事物的简化示例。

Java 代码:

package EMSResearch;

import java.sql.*;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

public class EMSResearch
{

    public static void main(String[] args)
    {
        Connection conn = null;
        Statement stmt = null;
        try
        {
            conn = DriverManager.getConnection("jdbc:sqlserver://MyDatabase;database=Emergencies;integratedsecurity=false;user=MyUserName;password=MyPassword");
            stmt = conn.createStatement();
            String strSelect = "SELECT EmergencyID, YearOfCall, ResponseTime, Ambulance";
            ResultSet rset = stmt.executeQuery(strSelect);

            DescriptiveStatistics ds = new DescriptiveStatistics();
/*the following code does the job of generating average response time for Medic1 for year 2015. But I want it to loop through and get all the ambulances for year 2015*/
            while (rset.next())
            {
                if (rset.getString("Ambulance").equals("Medic1") && rset.getInt("YearOfCall") == 2015)
                {
                    String event = rset.getString("I_EventNumber");
                    int year = rset.getInt("YearOfCall");
                    int responseTime = rset.getInt("ResponseTime");
                    String truck = rset.getString("Ambulance");
                    ds.addValue(responseTime);
                }
            }
            System.out.println("mean average value " + ds.getMean());


        } catch (SQLException ex)
        {
            ex.printStackTrace();
        } finally
        {

【问题讨论】:

  • 考虑改用SQL window functions
  • 这是我通常做的事情,但是在这种情况下,我想使用 Apache Commons Math,因为它具有 Skew 和 Kurtosis 的方法。而且,一旦我熟练了,我就可以做很多很酷的统计功能。

标签: java loops jdbc resultset apache-commons-math


【解决方案1】:

这样的事情可能会有所帮助。如果您使用地图存储所有年份和卡车的所有数据,我认为您可以获得所需的一切。 这段代码还没有完全出炉,但我认为它在概念上相当不错。

  private static void getstats(ResultSet rset) throws SQLException {
    Map<Integer, Map<String, DescriptiveStatistics>> stats = new HashMap<>();
    while (rset.next()) {

      String event = rset.getString("I_EventNumber");
      int year = rset.getInt("YearOfCall");
      int responseTime = rset.getInt("ResponseTime");
      String truck = rset.getString("Ambulance");
      if (stats.containsKey(year)) {
        Map<String, DescriptiveStatistics> get = stats.get(year);
        if (get.containsKey(truck)) {
          get.get(truck).addValue(responseTime);
        } else {
          Map<String, DescriptiveStatistics> newmap = new HashMap<>();
          DescriptiveStatistics newDs = new DescriptiveStatistics();
          newDs.addValue(responseTime);
          newmap.put(truck, newDs);
        }

      } else {

        Map<String, DescriptiveStatistics> newmap = new HashMap<>();
        DescriptiveStatistics newDs = new DescriptiveStatistics();
        newDs.addValue(responseTime);
        newmap.put(truck, newDs);
        stats.put(year, newmap);
      }

    }
    for(Integer year : stats.keySet()){
      for(String truck : stats.get(year).keySet()){
        DescriptiveStatistics ds = stats.get(year).get(truck);
        /**do stuff with the ds for this year and this truck**/

      }
    }

  }

【讨论】:

  • 谢谢markg:我要拿出我的大学教科书并在地图上阅读。我只是一个新手程序员。顺便说一句,我认为汤米男孩是一部搞笑电影。
  • 映射只是键->值结构。 get(...) 方法获取键并返回值,无论您在泛型类型参数中设置什么。地图功能强大,推荐大家了解一下!
【解决方案2】:

正如 markg 所说,Map 将极大地帮助您。不过,只是为了添加一点,我还会以有意义的方式对您的数据进行分组。例如,您当前的实现包含以下内容:

DescriptiveStatistics ds = new DescriptiveStatistics();

while (rset.next())
{
    if (rset.getString("Ambulance").equals("Medic1") && rset.getInt("YearOfCall") == 2015)
    {
        String event = rset.getString("I_EventNumber");
        int year = rset.getInt("YearOfCall");
        int responseTime = rset.getInt("ResponseTime");
        String truck = rset.getString("Ambulance");
        ds.addValue(responseTime);
    }
}

您现在实际上要做的是确定数据是否符合特定标准,然后将其添加到您的单个数据集中。但是,如果您想检查另一个条件,则需要初始化另一个数据集,添加另一个 if 语句,将代码复制到那里;它不可扩展

相反,请考虑创建一个对象,您可以通过以下方式对数据进行分组:

public class DataPoint {
    // Consider private members with public getters/setters.
    public String ambulance;
    public int year;

    public DataPoint(String ambulance, int year) {
        this.ambulance = ambulance;
        this.year = year;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((ambulance == null) ? 0 : ambulance.hashCode());
        result = prime * result + year;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        DataPoint other = (DataPoint) obj;
        if (ambulance == null) {
            if (other.ambulance != null)
                return false;
        } else if (!ambulance.equals(other.ambulance))
            return false;
        if (year != other.year)
            return false;
        return true;
    }
}

hashCode()equals() 覆盖很重要,但与此讨论无关。基本上,他们确保 Map 可以找到并确定具有相同参数的两个不同对象是相等的。

现在,使用我们的新 DataPoint 对象,我们可以将收到的数据映射到特定的数据集。所以我上面概述的你的实现将被替换为:

Map<DataPoint, DescriptiveStatistics> map = new HashMap<DataPoint, DescriptiveStatistics>();

while (rset.next())
{
    // Get parameters we differentiate based on.
    String truck = rset.getString("Ambulance");
    int year = rset.getInt("YearOfCall");

    // Create the data point.
    DataPoint point = new DataPoint(truck, year);

     // Get data set for point; if it doesn't exist, create it. 
    if (map.get(point) == null) {
        map.put(new DescriptiveStatistics());
    }
    DescriptiveStatistics ds = map.get(point);

    // Add the data of interest to the given data set.
    int responseTime = rset.getInt("ResponseTime");
    ds.addValue(responseTime);
}

当 while 循环结束时,您将拥有一个 Map,其中填充了特定数据点及其关联数据集的映射。从那里只需遍历地图条目,您就可以对数据集做任何您想做的事情:

for (Entry<DataPoint, DescriptiveStatistics> entry : map.entrySet())
...

希望能澄清一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多