How to calculate the mode from a table column (in an ImageJ macro)

Hello altogether,

is there any among the various ImageJ macros, that can calculate the mode from a table column?
I know that the distribution command offers the nice functionality to calculate the mode from any column of the results table, but I couldn’t find a possibility to access this value within a macro (e.g. by saving it into a variable).

I have seen the collection “BAR”, which even offers different algorithms to calculate the mode, but it also seems like the value is only included in the histogram image, but not accessible form a macro.

I assume for both - ImageJ’s distribution command as well as BAR - the code is written in Java so there’s no code snipped that I could include in a ImageJ macro.

Maybee I have overseen something. Has anyone a suggestion on how to best calculate the mode from any table column?

Thanks already!

1 Like

Hi @MojoDodo,

I’m afraid one has to program this with Array methods:

numbers = newArray(1,2,3,4,4,4,4,7,8,9);

Array.getStatistics(numbers, min, max, mean, stdDev);

mode = -1;
count_max = -1;
// go from min to max and count how often the numbers appear
for (search_num = min; search_num <= max; search_num++) { // works only with integer numbers
	count = 0;
	// count how often a given number appears
	for (j = 0; j < numbers.length; j++) {
		if (numbers[search_num] == numbers[j]) {
			count = count + 1;
		}
	}
	// keep the number which appears most often
	if (count > count_max) {
		count_max = count;
		mode = numbers[search_num];
	}
}

print("Min: " + min);
print("Max: " + max);
print("Mean: " + mean);
print("StdDev: " + stdDev);
print("Mode: " + mode);

Furthermore, for reading numbers from a table column, you can use Table.getColumn. Just use the macro auto-completion to find more Table and Array methods :wink:

Let us know if this helps!

Cheers,
Robert

1 Like

Thank you very much already for the help!
This is a nice approach, but (as you write in the comment) only works with integer numbers, which is a problem in my case. For instance, I need the mode from the Circularity column (and other shape parameters) after particle analysis.

Another approach I was fiddling with, was to use something like this (as discussed here):

...
run("Distribution...", "parameter=Area or=9999 and=0-0");
selectWindow("Area Distribution");
Plot.getValues(bins, counts);
//then find max from array "count" and get corresponding value of "bins"
...

However, this doesn’t seem to work in macros when I use setBatchMode(true); . I would get the error: No plot or histogram window in line XX : Plot . getValues ( bins , counts <)> ;
Is there any possibility to get the values from the Distribution plot while in batch mode?

1 Like

Not that I’m aware of. But if I understood you right, you want to make a histogram from a table column of numbers and then determine the histogram bin with the most entries?

You could actually adapt my script above. It goes from min to max in steps of 1. You could also move through this array from min to max in steps of (max - min)/numBins and count values…

1 Like

I just had to modify your macro a bit - it now also works with decimal numbers. In addition I’ve added an if-statement in the end to check whether count_max is still < 2 (i.e. no value in the array appeared more than once) and then set it to ND.

Here’s a quick demo:

run("Blobs (25K)");
setAutoThreshold("Default");
run("Analyze Particles...", "size=0-Infinity circularity=0.00-1.00 display clear add");

numbers = newArray(nResults);
for (j=0; j<nResults; j++) {
	//numbers[j] = getResult("Area", j);
	numbers[j] = getResult("Mean", j);
	//numbers[j] = getResult("Perim.", j);
	//numbers[j] = getResult("Circ.", j);
	//numbers[j] = getResult("Feret", j);
	}

mode = -1;
count_max = -1;
for (search_num = 0; search_num < numbers.length; search_num++) {
	count = 0;
	// count how often a given number appears
	for (j = 0; j < numbers.length; j++) {
		if (numbers[search_num] == numbers[j]) {
			count = count + 1;
		}
	}
	// keep the number which appears most often
	if (count > count_max) {
	//if (count > count_max && count < 2) {
		count_max = count;
		mode = numbers[search_num];
	}
}

if (count_max < 2) {
	mode = "ND";
}

	
print("Mode: " + mode);
1 Like

Just to check: Are you sure that’s the mode you’re determining? I don’t know the numbers you’re working with :wink:

Yes, the mode determined by the above posted code matches the values that I calculated with MS Excel and LibreOffice Calc - so at least for the data I tested it, it works as expected.
It should be noted that when several numbers occur equally often, the number that occurs first in the array will be determined as mode (Excel does so as well; LibreOffice seems to pick the smallest of the equally often occurring numbers)

1 Like

As an alternative, you can create a floating-point image with as many pixels as you have rows, i.e. 1 * nResults, populate it with the values of that column and then run “Measure” on the image (with ‘Modal gray value’ enabled in the Measurement Options). It will do binning into 256 bins, so the ‘mode’ may be also due to many nearby values that all appear only once.
–Michael

2 Likes

Sounds interesting, thanks!
For the moment I don’t see a reason to do it this way, but it’s surely worth keeping this approach in mind!