// Manage the values of the file, values are separated by space //Indices for the columns in the file "history.txt" /* 3B00080058124B10 - meeting room - 1 73000800B1D9BD10 - main lab - 2 DB000800B1E8E710 - machine room - 3 F2000800B1D88510 - lounge - 4 35000800B1EC2510 - prototype space - 5 DB00080041634E10 - classroom - 6 FF00080057FB1810 - office space - 7 */ class ReadingTable { String MAX_READING_DATE = "Aug/01/2011"; int rowCount; int rowCountAvg; int rowCountAvgXMonth; int columnCount; float[][] data; float[][] dataAvg; //Stores avg per day float[][] dataAvgXMonth; //Stores avg per month long[] rowNames; long[] rowNamesAvg; long[] rowNamesAvgXMonth; String[] columnNames = new String[]{"Meeting room","Main lab","Machine room","Lounge","Prototype space","Classroom","Office space"}; long minDate; long maxDate; float maxData; float minData; float maxDataAvg; float minDataAvg; float minDataAvgXMonth; float maxDataAvgXMonth; ReadingTable(String filename) { try { maxData = -Float.MAX_VALUE; minData = Float.MAX_VALUE; String[] rows = loadStrings(filename); //Columns are the indexes of the rooms columnCount = columnNames.length; rowNames = new long[rows.length]; data = new float[rows.length][columnCount]; for (int i = 0; i < rows.length; i++) { for (int j = 0; j < columnCount; j++){ data[i][j] = -Float.MAX_VALUE; } } long prevDate = 0; int countColumns = 0; //read data from the file and load the data to a table with rooms as columns and dates as rows. for (int i = 0; i < rows.length; i++) { if (trim(rows[i]).length() < 5) { continue; // skip the readings that are incomplete. } // split the row by blank spaces String[] reading = splitTokens(rows[i]); // copy row title (the whole date including time) chancing all minutes to :00 so all readings has the same time String strDateValue = reading[DATE]+" "+(split(reading[HOUR_MIN],":"))[0]+":00 "+reading[AM_PM]; long dateValue = readingDateFormat.parse(strDateValue).getTime(); if (strDateValue.equals("Dec/24/2009 10:00 AM")) println("Dec/24/2009 10:00 AM" + dateValue); if (dateValue > dateFormat.parse(MAX_READING_DATE).getTime()){ // Discard all dates after August 01 2011 maxDate = prevDate; break; } // increment the number of valid rows found so far if (prevDate != dateValue){ if(i > 0){ rowCount++; } prevDate = dateValue; } rowNames[rowCount] = dateValue; float value = parseFloat(reading[TEMPERATURE]); // Copy data in the column corresponding to the room. if (reading[ROOM_SENSOR].equals("3B00080058124B10")){ countColumns++; data[rowCount][0] = value; } else if (reading[ROOM_SENSOR].equals("73000800B1D9BD10")){ countColumns++; data[rowCount][1] = value; } else if (reading[ROOM_SENSOR].equals("DB000800B1E8E710")){ countColumns++; data[rowCount][2] = value; } else if (reading[ROOM_SENSOR].equals("F2000800B1D88510")){ countColumns++; data[rowCount][3] = value; } else if (reading[ROOM_SENSOR].equals("35000800B1EC2510")){ countColumns++; data[rowCount][4] = value; } else if (reading[ROOM_SENSOR].equals("DB00080041634E10")){ countColumns++; data[rowCount][5] = value; } else if (reading[ROOM_SENSOR].equals("FF00080057FB1810")){ countColumns++; data[rowCount][6] = value; } if (value < minData) { minData = value; } if (value > maxData) { maxData = value; } } // resize the 'data' array as necessary data = (float[][]) subset(data, 0, rowCount); rowNames = (long[]) subset (rowNames,0,rowCount); println("after resize minDate" + readingDateFormat.format(getDateFromMillis(rowNames[0])) ); maxDataAvg = -Float.MAX_VALUE; minDataAvg = Float.MAX_VALUE; maxDataAvgXMonth = -Float.MAX_VALUE; minDataAvgXMonth = Float.MAX_VALUE; int[] indexDay = new int[columnCount]; int[] indexMonth = new int[columnCount]; rowCountAvg = 0; rowCountAvgXMonth = 0; float[] sumXRoom = new float[columnCount]; float[] sumXRoomXMonth = new float[columnCount]; dataAvg = new float[rowCount][columnCount]; dataAvgXMonth = new float[rowCount][columnCount]; rowNamesAvg = new long [rowCount]; rowNamesAvgXMonth = new long [rowCount]; float value = 0; String prevMonth = ""; prevDate =0; // Once I have all data ready I can generate average by day for (int i = 0; i < rowCount; i++) { //i for the rows, that means for dates x hours // split the date by blank spaces to get only the date without time long dateValue = dateFormat.parse(dateFormat.format(getDateFromMillis(rowNames[i]))).getTime(); String monthValue = monthFormatToPrint.format(getDateFromMillis(dateValue)); if (prevDate != dateValue){ if(i > 0){ for (int j = 0; j < columnCount; j++){ // j for the columns, that means for rooms if (indexDay[j] > 0 ){ value = sumXRoom[j] / indexDay[j]; dataAvg[rowCountAvg][j] = value; rowNamesAvg[rowCountAvg] = prevDate; } if (value < minDataAvg) { minDataAvg = value; } if (value > maxDataAvg) { maxDataAvg = value; } sumXRoom[j] = 0; indexDay[j] = 0; } rowCountAvg++; } prevDate = dateValue; } if (!prevMonth.equals(monthValue)){ if(i > 0){ for (int j = 0; j < columnCount; j++){ // j for the columns, that means for rooms if (indexMonth[j] > 0 ){ value = sumXRoomXMonth[j] / indexMonth[j]; dataAvgXMonth[rowCountAvgXMonth][j] = value; rowNamesAvgXMonth[rowCountAvgXMonth] = rowNamesAvg[rowCountAvg -1] ; } if (value < minDataAvgXMonth) { minDataAvgXMonth = value; } if (value > maxDataAvgXMonth) { maxDataAvgXMonth = value; } sumXRoomXMonth[j] = 0; indexMonth[j] = 0; } rowCountAvgXMonth++; } prevMonth = monthValue; } for (int j = 0; j < columnCount; j++){ // j for the columns, that means for rooms if (data[i][j] > -Float.MAX_VALUE){ sumXRoom[j] = sumXRoom[j] + data[i][j]; sumXRoomXMonth[j] = sumXRoomXMonth[j] + data[i][j]; indexDay[j]++; indexMonth[j]++; } } } if (!(dateFormat.format(getDateFromMillis(rowNamesAvg[rowCountAvg-1]))).equals(dateFormat.format(getDateFromMillis(rowNamesAvgXMonth[rowCountAvgXMonth-1])))){ for (int j = 0; j < columnCount; j++){ // j for the columns, that means for rooms if (indexMonth[j] > 0 ){ value = sumXRoomXMonth[j] / indexMonth[j]; dataAvgXMonth[rowCountAvgXMonth][j] = value; rowNamesAvgXMonth[rowCountAvgXMonth] = rowNamesAvg[rowCountAvg -1] ; } if (value < minDataAvgXMonth) { minDataAvgXMonth = value; } if (value > maxDataAvgXMonth) { maxDataAvgXMonth = value; } sumXRoomXMonth[j] = 0; indexMonth[j] = 0; } rowCountAvgXMonth++; } // resize the 'dataAvg' and 'dataAvgXMonth' array as necessary dataAvg = (float[][]) subset(dataAvg, 0, rowCountAvg); rowNamesAvg = (long[]) subset(rowNamesAvg,0,rowCountAvg); dataAvgXMonth = (float[][]) subset(dataAvgXMonth, 0, rowCountAvgXMonth); rowNamesAvgXMonth = (long[]) subset(rowNamesAvgXMonth,0,rowCountAvgXMonth); minDate = rowNames[0]; maxDate = rowNames[rowCount-1]; } catch (ParseException e) { //println("Error"); die("Problem loading the file", e); } //println("File uploaded --> rowCount = " + rowCount + " , rowCountAvg = " + rowCountAvg); } int getRowCount() { if (showData == 'D') return rowCountAvg; else if (showData == 'H') return rowCount; return rowCountAvgXMonth; } long getRowName(int rowIndex) { if (showData == 'D') return rowNamesAvg[rowIndex]; else if (showData == 'H') return rowNames[rowIndex]; return rowNamesAvgXMonth[rowIndex]; } long[] getRowNames() { if (showData == 'D') return rowNamesAvg; else if (showData == 'H') return rowNames; return rowNamesAvgXMonth; } // Find a row by its name, returns -1 if no row found. // This will return the index of the first row with this name. // A more efficient version of this function would put row names // into a Hashtable (or HashMap) that would map to an integer for the row. int getRowIndex(String name) { try{ if (showData == 'D'){ for (int i = 0; i < rowCountAvg; i++) { // println(rowNamesAvg[i]) if (rowNamesAvg[i] == (dateFormat.parse(name)).getTime()) { return i; } } ////println("No row named '" + name + "' was found"); return -1; } else if (showData == 'H'){ // Look for the same date (time included) for (int i = 0; i < rowCount; i++) { if (rowNames[i] == (dateFormat.parse(name)).getTime()) { return i; } } ////println("No row named '" + name + "' was found"); return -1; } //Look for the same month a year. for (int i = 0; i < rowCountAvgXMonth; i++) { if ( rowNamesAvgXMonth[i] == (dateFormat.parse(name)).getTime()) { return i; } } ////println("No row named '" + name + "' was found"); return -1; } catch (ParseException e) { //println("Error"); die("Problem loading the file", e); return -1; } } int getRowMinIndex(long name) { println(readingDateFormat.format(getDateFromMillis(name)) + " " + rowCount + " " + rowCountAvg + " " + rowCountAvgXMonth); return 0; ////println("No row named '" + name + "' was found"); } int getRowMinIndex(String name) { try{ long dateName; dateName = (readingDateFormat.parse(name +" 12:00 AM")).getTime(); if (showData == 'D'){ println("Min "+dateName ); if (rowNamesAvg[rowCountAvg-1] <= dateName) return rowCountAvg - 1; if (dateName <= rowNamesAvg[0]) return 0; int dayIndex = (int)((dateName - rowNamesAvg[0]) / MILLIS_PER_DAY); // index to help to go fast in the array. if (dayIndex >= rowCountAvg) dayIndex = rowCountAvg -1; println(dayIndex + "-->" + dateFormat.format(rowNamesAvg[dayIndex]) + " " + rowCountAvg); for (int i = dayIndex; i >= 0; i--) { if (rowNamesAvg[i] <= dateName) { println (dateFormat.format(rowNamesAvg[i]) + " " + i); return i; } } } else if (showData == 'H'){ // Look for the same date (time included) if (rowNames[rowCount-1] <= dateName) return rowCount - 1; if (dateName <= rowNames[0]) return 0; int hourIndex = (int)((dateName - rowNames[0]) / MILLIS_PER_HOUR); // index to help to go fast in the array. if (hourIndex >= rowCount) hourIndex = rowCount - 1; for (int i = hourIndex; i >= 0; i--) { if (rowNames[i] <= dateName) { println (dateFormat.format(rowNames[i]) + " " + i); return i; } } ////println("No row named '" + name + "' was found"); return -1; } //Look for the same month a year. if (rowNamesAvgXMonth[rowCountAvgXMonth-1] <= dateName) return rowCountAvgXMonth - 1; if (dateName <= rowNamesAvgXMonth[0]) return 0; int yearIndex = (int(yearFormatToPrint.format(dateName)) - int(yearFormatToPrint.format(minDate))) * 12; // index to help to go fast in the array. if (yearIndex >= rowCountAvgXMonth) yearIndex = rowCountAvgXMonth - 1; for (int i = yearIndex; i >= 0; i--) { if (rowNamesAvgXMonth[i] <= dateName) { return i; } } ////println("No row named '" + name + "' was found"); return -1; } catch (ParseException e) { //println("Error"); die("Problem loading the file", e); return -1; } } int getRowMaxIndex(String name) { try{ long dateName; if (showByDay) dateName = (readingDateFormat.parse(name +" 11:00 PM")).getTime(); else dateName = (readingDateFormat.parse(name +" 12:00 AM")).getTime(); if (showData == 'D'){ if (rowNamesAvg[rowCountAvg-1] <= dateName) return rowCountAvg - 1; if (dateName <= rowNamesAvg[0]) return 0; int dayIndex = (int)((dateName - rowNamesAvg[0]) / MILLIS_PER_DAY); // index to help to go fast in the array. if (dayIndex >= rowCountAvg) dayIndex = rowCountAvg - 1; for (int i = dayIndex; i >= 0; i--) { if (rowNamesAvg[i] <= dateName) { return i; } } } else if (showData == 'H'){ // Look for the same date (time included) if (rowNames[rowCount-1] <= dateName) return rowCount - 1; if (dateName <= rowNames[0]) return 0; int hourIndex = (int)((dateName - rowNames[0]) / MILLIS_PER_HOUR); // index to help to go fast in the array. if (hourIndex >= rowCount) hourIndex = rowCount -1; for (int i = hourIndex; i >= 0; i--) { if (rowNames[i] <= dateName) { println (dateFormat.format(rowNames[i]) + " " + i); return i; } } ////println("No row named '" + name + "' was found"); return -1; } //Look for the same month a year. if (dateName <= rowNamesAvgXMonth[0]) return 0; if (rowNamesAvgXMonth[rowCountAvgXMonth-1] <= dateName) return rowCountAvgXMonth - 1; int yearIndex = (int(yearFormatToPrint.format(dateName)) - int(yearFormatToPrint.format(minDate)) + 1) * 12; // index to help to go fast in the array. println(yearIndex + "-->" + dateName); if (yearIndex >= rowCountAvgXMonth) yearIndex = rowCountAvgXMonth - 1; for (int i = yearIndex; i >= 0; i--) { if (rowNamesAvgXMonth[i] <= dateName) { return i; } } ////println("No row named '" + name + "' was found"); return -1; } catch (ParseException e) { //println("Error"); die("Problem loading the file", e); return -1; } } int getRowMaxIndex(long name) { if (showData == 'D') return rowCountAvg - 1; if (showData == 'H') // Look for the same date (time included) return rowCount - 1; else return rowCountAvgXMonth - 1; } // technically, this only returns the number of columns // in the very first row (which will be most accurate) int getColumnCount() { return columnCount; } long getMinDate() { /* println("Entro "+readingDateFormat.format(getDateFromMillis(rowNames[0]))); if (showData == 'D') return rowNamesAvg[0]; else if (showData == 'H') return rowNames[0]; else return rowNamesAvgXMonth[0]; */ return rowNames[0]; } long getMaxDate() { /* println("Entro "+readingDateFormat.format(getDateFromMillis(rowNames[rowCount-1]))); if (showData == 'D') return rowNamesAvg[rowCountAvg-1]; else if (showData == 'H') return rowNames[rowCount-1]; else return rowNamesAvgXMonth[rowCountAvgXMonth-1];*/ return rowNames[rowCount-1]; } String getColumnName(int colIndex) { return columnNames[colIndex]; } String[] getColumnNames() { return columnNames; } float getFloat(int rowIndex, int col) { // Remove the 'training wheels' section for greater efficiency // It's included here to provide more useful error messages if (showData == 'D'){ // begin training wheels if ((rowIndex < 0) || (rowIndex >= dataAvg.length)) { throw new RuntimeException("There is no row " + rowIndex); } if ((col < 0) || (col >= dataAvg[rowIndex].length)) { throw new RuntimeException("Row " + rowIndex + " does not have a column " + col); } // end training wheels //// It is going to show the data by hours as it is on the file history.txt if (CorF=='F') return round(dataAvg[rowIndex][col]); // Give value in F grades else return round(MULTIPLICATOR_VALUE * (dataAvg[rowIndex][col] - 32)); // Give value in C grades } else if (showData == 'H'){ // begin training wheels if ((rowIndex < 0) || (rowIndex >= data.length)) { throw new RuntimeException("There is no row " + rowIndex); } if ((col < 0) || (col >= data[rowIndex].length)) { throw new RuntimeException("Row " + rowIndex + " does not have a column " + col); } // end training wheels if (CorF=='F') return round(data[rowIndex][col]); // Give value in F grades else return round(MULTIPLICATOR_VALUE * (data[rowIndex][col] - 32)); // Give value in C grades } // begin training wheels if ((rowIndex < 0) || (rowIndex >= dataAvgXMonth.length)) { throw new RuntimeException("There is no row " + rowIndex); } if ((col < 0) || (col >= dataAvgXMonth[rowIndex].length)) { throw new RuntimeException("Row " + rowIndex + " does not have a column " + col); } // end training wheels //// It is going to show the data by hours as it is on the file history.txt if (CorF=='F') return round(dataAvgXMonth[rowIndex][col]); // Give value in F grades else return round(MULTIPLICATOR_VALUE * (dataAvgXMonth[rowIndex][col] - 32)); // Give value in C grades } boolean isValid(int row, int col) { if (row < 0) return false; if (col < 0) return false; if (col >= columnCount) return false; if (showData == 'D'){ if (row >= rowCountAvg) return false; return !Float.isNaN(dataAvg[row][col]); } else if (showData == 'H'){ if (row >= rowCount) return false; return !Float.isNaN(data[row][col]); } if (row >= rowCountAvgXMonth) return false; return !Float.isNaN(dataAvgXMonth[row][col]); } float getColumnMin(int col) { float m = Float.MAX_VALUE; for (int row = 0; row < rowCount; row++) { if (isValid(row, col)) { if (data[row][col] < m) { m = data[row][col]; } } } return m; } float getColumnMax(int col) { float m = -Float.MAX_VALUE; for (int row = 0; row < rowCount; row++) { if (isValid(row, col)) { if (data[row][col] > m) { m = data[row][col]; } } } return m; } float getRowMin(int row) { float m = Float.MAX_VALUE; for (int col = 0; col < columnCount; col++) { if (isValid(row, col)) { if (data[row][col] < m) { m = data[row][col]; } } } return m; } float getRowMax(int row) { float m = -Float.MAX_VALUE; for (int col = 0; col < columnCount; col++) { if (isValid(row, col)) { if (data[row][col] > m) { m = data[row][col]; } } } return m; } float getTableMin() { if (showData == 'D'){ if (CorF=='F') return round(minDataAvg); else return round(MULTIPLICATOR_VALUE * (minDataAvg - 32)); // Give value in C grades } else if (showData == 'H'){ if (CorF=='F') return round(minData); else return round(MULTIPLICATOR_VALUE * (minData - 32)); // Give value in C grades } if (CorF=='F') return round(minDataAvgXMonth); else return round(MULTIPLICATOR_VALUE * (minDataAvgXMonth - 32)); // Give value in C grades } float getTableMax() { if (showData == 'D'){ if (CorF=='F') return round(maxDataAvg); else return round(MULTIPLICATOR_VALUE * (maxDataAvg - 32)); // Give value in C grades } else if (showData == 'H'){ if (CorF=='F') return round(maxData); else return round(MULTIPLICATOR_VALUE * (maxData - 32)); // Give value in C grades } if (CorF=='F') return round(maxDataAvgXMonth); else return round(MULTIPLICATOR_VALUE * (maxDataAvgXMonth - 32)); // Give value in C grades*/ } void savePredictions(int _month,String _year){ // I am assuming weather have a trend. Then in theory every month should have the same temperature per year. // As this is not true. I compute PE the error of the reading comparing to the previous reading. I compute average of this error // and to predict the next month I multiply the previous month by (1-PE). float[] pError = new float[columnCount]; float[] avgPError = new float[columnCount]; float[] sumPError = new float[columnCount]; float countError = 0; int indexPrevAVG = -1; String monthValue=""; // compute error of specific month averages. for (int i = 0; i < rowCountAvgXMonth; i++) { monthValue = shortMonthFormatToPrint.format(getDateFromMillis(rowNamesAvgXMonth[i])); if (int(monthValue) == _month){ if(indexPrevAVG > -1){ for (int j = 0; j < columnCount; j++){ // j for the columns, that means rooms pError[j] = (dataAvgXMonth[indexPrevAVG][j] - dataAvgXMonth[i][j])/dataAvgXMonth[indexPrevAVG][j]; sumPError[j] = sumPError[j] + (1 - pError[j]); } countError++; } indexPrevAVG = i; } } //get the avg of the errors. if (indexPrevAVG == -1){ println("There is no history for the month, the prediction cannot be computed"); } else{ for (int j = 0; j < columnCount; j++){ //compute average error. if (countError == 0) avgPError[j] = 0; else avgPError[j] = sumPError[j] / countError; } //Print in the console the predictions. println("Predictions for " + _month +"/"+_year); println("3B00080058124B10,"+round(dataAvgXMonth[indexPrevAVG][0]*avgPError[0])); println("73000800B1D9BD10,"+round(dataAvgXMonth[indexPrevAVG][1]*avgPError[1])); println("DB000800B1E8E710,"+round(dataAvgXMonth[indexPrevAVG][2]*avgPError[2])); println("F2000800B1D88510,"+round(dataAvgXMonth[indexPrevAVG][3]*avgPError[3])); println("35000800B1EC2510,"+round(dataAvgXMonth[indexPrevAVG][4]*avgPError[4])); println("DB00080041634E10,"+round(dataAvgXMonth[indexPrevAVG][5]*avgPError[5])); println("FF00080057FB1810,"+round(dataAvgXMonth[indexPrevAVG][6]*avgPError[6])); } } }