Python: Get Average Of Two Dimensional List – No Imports [Code Examples]


In my previous article where I found the average of a list in Python without using imports, in this article I want to expand how you could apply the same concept, but to two-dimensional lists. Thankfully, if you know how to find the average of a one dimensional list in Python, you can simply expand this knowledge to finding the average of a two-dimensional list.

The big question to ask, when finding the average of a two-dimensional list, is seeking what is it that you want to average: is the whole list, or just an average of each row, or is it an average of each list?

Here’s a simple 2-dimensional list showing some results according to what type of average is sought:

[[1, 2, 3],
 [3, 4, 5],
 [5, 6, 13]]
# average of each cell = 36 / 9 = 4.6667
# average of each row = (6 + 12 + 18) / 3 = 14
# average of each column = (9 + 12 + 21) / 3 = 14
# average of each row as list = [2, 4, 8]
# average of each column as list = [3, 4, 7]
# average of the rows = (2 + 4 + 8) / 3 = 4.6667
# average of the columns = (3 + 4 + 7) = 4.6667

To calculate the average of each cell, all that is needed is to obtain the values of all numeric cells within the 2D list, and then to sum the contents of those numeric values and divide by the quantity of numeric values.

We can easily achieve this feat by capturing all values into a list, and then apply what we’ve already learned previously by averaging a list in Python without using any libraries.

But how would we go about calculating the average of each row, or column within a 2D list in Python?

Could the same concept be applied to calculating the average of these elements within a 2D list?

Yes, they can!

Calculate Average Each Row & Column

To calculate the average of each row or column in a 2D list we need to store the contents of numeric values in each row and column.

The best way to store these values is by using a dict to operate as the row or column identifier and to then append all numeric values into that dict.

Once we have a list of numbers we can then simply apply what we already know on how to obtain an average of a list.

Let’s create a function that can create all of these averages, we’ll use a function to help coordinate the calculation.

def calculate_averages(list_2d):
    cell_total = list()
    row_totals = dict()
    column_totals = dict()
    for row_idx, row in enumerate(list_2d):
        for cell_idx, cell in enumerate(row):
            # is cell a number?
            if type(cell) in [int, float, complex]:
                cell_total.append(cell)                
                if row_idx in row_totals:
                    row_totals[row_idx].append(cell)
                else:
                    row_totals[row_idx] = [cell]
                if cell_idx in column_totals:
                    column_totals[cell_idx].append(cell)
                else:
                    column_totals[cell_idx] = [cell]
    per_row_avg = [sum(row_totals[row_idx]) / len(row_totals[row_idx]) for row_idx in row_totals]
    per_col_avg = [sum(column_totals[col_idx]) / len(column_totals[col_idx]) for col_idx in column_totals]
    row_avg = sum(per_row_avg) / len(per_row_avg)
    col_avg = sum(per_col_avg) / len(per_col_avg)
    return {'cell_average': sum(cell_total) / len(cell_total),
            'per_row_average': per_row_avg,
            'per_column_average': per_col_avg,
            'row_average': row_avg,
            'column_average': col_avg}


data = [['this', 'is a', 'header row'], [1, 2, 3], [3, 4, 5], [5, 6, 13]]

result = calculate_averages(data)

print(result)

# {'cell_average': 4.666666666666667, 'per_row_average': [2.0, 4.0, 8.0], 'per_column_average': [3.0, 4.0, 7.0], 'row_average': 4.666666666666667, 'column_average': 4.666666666666667}

This function performs a loop through each row, and then through each cell in the row. Hence, why we have two for-loops to match the dimensions of the two-dimensional list.

All operations are performed on the cell level where we first check the value of the cell is numeric. As you can see with the data variable I have added a header row to the 2D list, as these are not numeric these items need to be filtered out from the average calculations.

Once we’ve filtered the numeric values, we then append this to our cell_total list – this is the list that will provide the simple average of all numeric values in the list.

Then we move to the row and column section, as shown below:

if row_idx in row_totals:
    row_totals[row_idx].append(cell)
else:
    row_totals[row_idx] = [cell]
if cell_idx in column_totals:
    column_totals[cell_idx].append(cell)
else:
    column_totals[cell_idx] = [cell]

In this section we check if an identifer representing the row or the column exists on the dictionary for each subsequent row_totals and column_totals. The identifier in this case is the row_idx or the cell_idx respectively.

If these properties already exist on the dictionary, then we append another element to the existing list, if not we then create a list with the first cell value.

The final section of our function before outputting the result, is this:

per_row_avg = [sum(row_totals[row_idx]) / len(row_totals[row_idx]) for row_idx in row_totals]
per_col_avg = [sum(column_totals[col_idx]) / len(column_totals[col_idx]) for col_idx in column_totals]
row_avg = sum(per_row_avg) / len(per_row_avg)
col_avg = sum(per_col_avg) / len(per_col_avg)

Here we have a one-liner for-loop that takes the results of each list in our compiled row_totals and column_totals dictionaries and performs the simple average needed: total the values inside the list and then divide by the quantity of items in the list.

The per_row_avg variable produces a list representing the average of each row. The only issue here being, that it does not display a None value for any non-numeric rows. The same applies to the per_col_avg variable too.

Finally, to obtain an average of a row we obtain the lists of per_row_avg and per_col_avg and then, once again, apply the simple average calculation of summing the contents of the list and dividing by the quantity of values in that list. Now we have an average per row, and an average per column.

Summary

The trick to help manage the calculation of a two-dimensional list, where it’s uncertain if the list contains proper values to calculate an average, is to store each of the rows and columns in a dict during the operation, and then to store numeric values into a list.

In this article we’ve also shown how you can calculate the total average of all numeric values within a 2D list using Python’s functions (no imports needed), and how to apply the same logic to calculate the averages of rows and columns for your 2D list.

Here is just the function which you would call calculate_averages(your_2d_list) and insert your 2D list in the parameters:

def calculate_averages(list_2d):
    cell_total = list()
    row_totals = dict()
    column_totals = dict()
    for row_idx, row in enumerate(list_2d):
        for cell_idx, cell in enumerate(row):
            # is cell a number?
            if type(cell) in [int, float, complex]:
                cell_total.append(cell)                
                if row_idx in row_totals:
                    row_totals[row_idx].append(cell)
                else:
                    row_totals[row_idx] = [cell]
                if cell_idx in column_totals:
                    column_totals[cell_idx].append(cell)
                else:
                    column_totals[cell_idx] = [cell]
    per_row_avg = [sum(row_totals[row_idx]) / len(row_totals[row_idx]) for row_idx in row_totals]
    per_col_avg = [sum(column_totals[col_idx]) / len(column_totals[col_idx]) for col_idx in column_totals]
    row_avg = sum(per_row_avg) / len(per_row_avg)
    col_avg = sum(per_col_avg) / len(per_col_avg)
    return {'cell_average': sum(cell_total) / len(cell_total),
            'per_row_average': per_row_avg,
            'per_column_average': per_col_avg,
            'row_average': row_avg,
            'column_average': col_avg}

Ryan

Author of scripteverything.com, Ryan has been dabbling in code since the late '90s when he cut his teeth by exploring VBA in Excel when trying to do something more. Having his eyes opened with the potential of automating repetitive tasks, he expanded to Python and then moved over to scripting languages such as HTML, CSS, Javascript and PHP. When he is not behind a screen, Ryan enjoys a good bush walk with the family during the cooler months, and going with them to the beach during the warmer months.

Recent Posts