macro "Table Calculator and Sorter"{ /* v180501 first version posted to http://forum.imagej.net May 1st 2018 This version v180523 adds option to open table if none open v181207 Adds new Table.sort command introduced in 1.52i v200605 Adds column renaming v200609 Adds column deletion Renamed Table_Tools to reflect additional tools v221208 Adds % Deviation from Mean. f1: Updates indexOf functions. v231013 Headers on by split by tabs. Prefs added. Blank first column header fix. Constant can now be non-integer. v231016 Column export and import with header options added. b-c: More flexible import options. Better organization of main dialog. F1: updated function unCleanLabel. */ macroL = "ASC-Table_Utilities_v231016c-f1.ijm"; requires("1.52i"); /* We are using the new Table Macro Functions released in 1.52a and Table.sort from 1.52i */ ascPrefsKey = "asc.TableUtilities.Prefs."; nRes = Table.size; if (nRes==0) { tableDecision = getBoolean("This macro requires a table, do you want to open one?", "Yes", "No"); if (tableDecision==true) { Table.open(""); nRes = Table.size; } else exit(); } if (nRes==0) exit("Still no table, goodbye"); headings = split(Table.headings,"\t"); /* Headings are tab-delimited */ if (headings[0]==" " || headings[0]=="") headings = Array.deleteIndex(headings, 0); operations = newArray("No Change", "Add", "Subtract", "Multiply", "Divide", "Min", "Max", "Average", "AbsDiff", "PercentOf", "%DevFromMean"); /* Same operations as Image Calculator */ /* ASC Dialog style: */ infoColor = "#006db0"; /* Honolulu blue */ instructionColor = "#798541"; /* green_dark_modern (121,133,65) AKA Wasabi */ infoWarningColor = "#ff69b4"; /* pink_modern AKA hot pink */ infoFontSize = 12; Dialog.create("Table Calculator and Utilities \(" + macroL + "\)"); Dialog.addMessage("______Table Calculator: Create new column based on existing column values________"); Dialog.addMessage("For Value 1 select column to operate on \(or 'No Change' operator below\)", infoFontSize, instructionColor); iValue1 = indexOfArray(headings, call("ij.Prefs.get", ascPrefsKey + "value1", headings[0]), 0); Dialog.addChoice("Value 1", headings, headings[iValue1]); iValue2 = indexOfArray(headings, call("ij.Prefs.get", ascPrefsKey + "value2", headings[1]), 0); Dialog.addChoice("Value 2", headings, headings[iValue2]); Dialog.setInsets(0, 0, 5); Dialog.addNumber("Constant for Value 2", NaN, 0, 10, "Any value entered here replaces Value 2"); iOperation = indexOfArray(operations, call("ij.Prefs.get", ascPrefsKey + "operation", operations[0]), 0); rCols = 6; rRows = Math.ceil(operations.length/rCols); Dialog.addRadioButtonGroup(" Operation:____________________________", operations, rRows, rCols, operations[0]); Dialog.setInsets(0, 20, 5); Dialog.addMessage("If '%DevFromMean' is chosen 'Value 2' and 'Constant' are ignored", infoFontSize, instructionColor); Dialog.addMessage("New column name is generated from operation, if you want a different name enter it below:", infoFontSize, instructionColor); Dialog.setInsets(0, 20, 0); Dialog.addString("Alternative column name:","", 50); Dialog.setInsets(0, 20, 5); Dialog.addMessage("Column names will be 'cleaned', i.e. 'um' becomes " + fromCharCode(181) + "m, '^2' becomes " + fromCharCode(178), infoFontSize, instructionColor); Dialog.setInsets(0, 20, 5); Dialog.addMessage("______Table Utilities: Actions will be performed after any calculation above________"); bonusOperations = newArray("Sort table \(new dialog will open\)?", "Rename up to 10 column names?", "Delete up to 15 named columns?", "Fill selected columns with single value?"); bonusOperationChecks = newArray(false, false, false, false); Dialog.setInsets(0, 20, 0); Dialog.addCheckboxGroup(3, 2, bonusOperations, bonusOperationChecks); /* Import/Export */ Dialog.setInsets(0, 20, 5); Dialog.addMessage("______Column Export Options \(tab separated value file will be saved\):________________"); tPath = ""; t = ""; if (nImages>0){ t = getTitle(); tPath = getInfo("image.directory"); } if (tPath=="") tPath = File.directory; if (indexOf(tPath, "AutoRun")>=0) tPath = ""; inOutChoices = Array.concat("---No Export---", "Calculated Column", headings); Dialog.addChoice("Column for export", inOutChoices, inOutChoices[0]); Dialog.addDirectory("Export path", tPath); Dialog.addCheckbox("Add column name as header of exported file", true); Dialog.addString("File prefix", t, 50); Dialog.setInsets(0, 20, 10); Dialog.addMessage("Column name and .tsv will be added after the prefix", infoFontSize, instructionColor); Dialog.setInsets(0, 20, 5); Dialog.addMessage("______Column Import Options \(delimited text file expected\):________________"); Dialog.addFile("Import path", "---No Import---"); Dialog.addCheckbox("If import selected assume file starts with a header name", true); Dialog.addString("New column name", "---Use Name in Header---", 50); Dialog.addCheckbox("Import values as numbers \(10 decimal places\)", true); Dialog.addCheckbox("Diagnostics", false); Dialog.show; value1 = Dialog.getChoice; call("ij.Prefs.set", ascPrefsKey + "value1", value1); value2 = Dialog.getChoice; call("ij.Prefs.set", ascPrefsKey + "value2", value2); constant = Dialog.getNumber; if (constant!=NaN) value2 = "Constant"; operation = replace(Dialog.getRadioButton(),"%",""); call("ij.Prefs.set", ascPrefsKey + "operation", operation); newName = Dialog.getString; sortByColumn = Dialog.getCheckbox(); renameColumns = Dialog.getCheckbox(); deleteColumns = Dialog.getCheckbox(); fillColumns = Dialog.getCheckbox(); exportColumn = Dialog.getChoice(); addHeader = Dialog.getCheckbox(); exportPath = Dialog.getString(); filePrefix = Dialog.getString(); importPath = Dialog.getString(); hasHeader = Dialog.getCheckbox(); importHeader = cleanLabel(Dialog.getString()); if (importHeader=="---Use Name in Header---") importHeader = ""; else importHeader = cleanLabel(importHeader); importNumbers = Dialog.getCheckbox(); diagnostics = Dialog.getCheckbox(); if (diagnostics) IJ.log("value1 :" + value1); if (diagnostics) IJ.log("value2 :" + value2); nonIntC = false; if (operation!="No Change") { if (operation=="DevFromMean"){ values1 = Table.getColumn(value1); Array.getStatistics(values1, values1min, values1max, values1mean, values1stdDev); pcMult = 100/values1mean; formula = value1 + "*" + pcMult + "-100"; if (diagnostics) IJ.log("formula :" + formula); newColumn = "" + value1 + "_Dev_from_Mean_Percentage_"; /* Create new column name */ code = newColumn + "=" + formula; } else if (value2=="Constant"){ if (round(constant)!=constant) nonIntC = true; } if (operation!="DevFromMean") { if (nonIntC){ constants = newArray(nRes); constants = Array.fill(constants, constant); Table.setColumn("tempConstant", constants); value2 = "tempConstant"; } value1or = value1; value2or = value2; value1 = opsToAbb(value1); value2 = opsToAbb(value2); if (value1!=value1or) Table.renameColumn(value1, value1or); if (value2!=value2or) Table.renameColumn(value2, value2or); newColumn = "" + value1 + opsToAbb(operation) + value2; /* Create new column name */ code = "" + newColumn + "=" + formulaFromOp(operation, value1, value2); } if (diagnostics) IJ.log("newColumn :" + newColumn); if (diagnostics) IJ.log("Table operation macro code: " + code); Table.applyMacro(code); Table.update; if (nonIntC) Table.deleteColumn(value2); else if (operation!="DevFromMean"){ if (value1!=value1or) Table.renameColumn(value1, value1or); if (value2!=value2or) Table.renameColumn(value2, value2or); } Table.update; if (operation=="DevFromMean"){ if (newName=="") newColumnName = replace(newColumn, "_Percentage_", "\(" + fromCharCode(037) + "\)"); else newColumnName = cleanLabel(newName); if (indexOfArray(headings,newColumnName,-1)>=0) Table.deleteColumn(newColumnName); Table.renameColumn(newColumn,newColumnName); } else if (newName!=""){ newColumnName = cleanLabel(newName); if (indexOfArray(headings, newColumnName, -1)>=0) Table.deleteColumn(newColumnName); Table.renameColumn(newColumn, newColumnName); } headings = split(Table.headings, "\t"); /* Updates array of table column headers */ if (headings[0]==" " || headings[0]=="") headings = Array.deleteIndex(headings, 0); if (exportColumn=="Calculated Column") exportColumn = newColumnName; } if (exportColumn!="---No Export---"){ exportColumnValues = Table.getColumn(exportColumn); exportString = arrayNumbersToString(exportColumnValues, 10, "\t"); if (addHeader) exportString = exportColumn + "\t" + exportString; fS = File.separator; if (!endsWith(exportPath,fS)) exportPath += fS; fileName = unCleanLabel(filePrefix + "_" + exportColumn + ".tsv"); exportPath += fileName; File.saveString(exportString, exportPath); } if (importPath!="---No Import---"){ importString = File.openAsString(importPath); while (endsWith(importString,"\n")) importString = substring(importString, 0, importString.length-1); while (endsWith(importString,"\r")) importString = substring(importString, 0, importString.length-1); iSL = importString.length; delimType = ","; cCount = iSL - lengthOf(replace(importString, ",", "")); tCount = iSL - lengthOf(replace(importString, "\t", "")); if (tCount>cCount) delimType = "\t"; nCount = iSL - lengthOf(replace(importString, "\n", "")); if (nCount>cCount && nCount>tCount) delimType = "\n"; rCount = iSL - lengthOf(replace(importString, "\r", "")); if (rCount>cCount && rCount>tCount && rCount>nCount) delimType = "\r"; if (hasHeader){ impHeader = substring(importString, 0, indexOf(importString, delimType)); if (importHeader=="") importHeader = impHeader; importString = substring(importString, indexOf(importString, delimType) + 1); } importValues = split(importString, delimType); if (importValues.length<2) importValues = split(importString); if (importValues.length>0){ if (importNumbers){ importNumbers = newArray(); for (i=0; i1) string = substring(string, 0, indexOf(string,"mý")-1) + getInfo("micrometer.abbreviation") + fromCharCode(178); return string; } function formulaFromOp(op, val1,val2) { /* v231013-4: Added Percent off and fixed end of AbsDiff line */ if (op=="Add") formula = "" + val1 + "+" + val2; if (op=="Subtract") formula = "" + val1 + "-" + val2; if (op=="Multiply") formula = "" + val1 + "*" + val2; if (op=="Divide") formula = "" + val1 + "/" + val2; if (op=="Min") formula = "minOf\(" + val1 + "," + val2 + "\)"; if (op=="Max") formula = "maxOf\(" + val1 + "," + val2 + "\)"; if (op=="Average") formula = "\(" + val1 + "+" + val2 + "\)/2"; if (op=="AbsDiff") formula = "abs\(" + val1 + "-" + val2 + "\)"; if (op=="PercentOf") formula = "" + val1 + "*100/" + val2; return formula; } function indexOfArray(array, value, default) { /* v190423 Adds "default" parameter (use -1 for backwards compatibility). Returns only first found value v230902 Limits default value to array size */ index = minOf(lengthOf(array) - 1, default); for (i=0; i=0) { string = string.substring(0, iFirst) + string.substring(string, iFirst + lengthOf(unwantedDbls[i]) / 2); i = -1; /* check again */ } } string = string.replace("_\\+", "\\+"); /* Clean up autofilenames */ string = string.replace("\\+_", "\\+"); /* Clean up autofilenames */ /* cleanup suffixes */ unwantedSuffixes = newArray(" ", "_", "-", "\\+"); /* things you don't wasn't to end a filename with */ extStart = lastIndexOf(string, "."); sL = lengthOf(string); if (sL-extStart<=4 && extStart>0) extIncl = true; else extIncl = false; if (extIncl){ preString = substring(string, 0, extStart); extString = substring(string, extStart); } else { preString = string; extString = ""; } for (i=0; i