Skip to main content

How to Find the Most Common Element in Each Column of a 2D List in Python

Finding the mode (most frequent value) for each column in a matrix is a common task in data analysis, preprocessing, and feature engineering. Given a 2D list where each inner list represents a row, you need to determine which value appears most often in each column position.

In this guide, you will learn how to solve this problem using zip() with Counter, handle tie-breaking scenarios, work with different data types, and use Pandas for larger datasets.

Using zip() and Counter

The most Pythonic approach combines zip(*matrix) for transposition with Counter for frequency counting:

from collections import Counter

matrix = [
[1, 2, 3],
[4, 2, 3],
[1, 5, 3],
]

result = [Counter(col).most_common(1)[0][0] for col in zip(*matrix)]

print(result)

Output:

[1, 2, 3]

Column 0 contains [1, 4, 1], where 1 appears twice. Column 1 contains [2, 2, 5], where 2 appears twice. Column 2 contains [3, 3, 3], where 3 appears three times.

Breaking Down the Solution Step by Step

Each step in the one-liner performs a specific transformation:

from collections import Counter

matrix = [
[1, 2, 3],
[4, 2, 3],
[1, 5, 3],
]

# Step 1: Transpose rows into columns
columns = list(zip(*matrix))
print(columns)

# Step 2: Count frequencies in each column
for i, col in enumerate(columns):
counter = Counter(col)
print(f"Column {i}: {dict(counter)}")

# Step 3: Extract the most common element from each column
for col in zip(*matrix):
most_common = Counter(col).most_common(1)
print(most_common)

Output:

[(1, 4, 1), (2, 2, 5), (3, 3, 3)]
Column 0: {1: 2, 4: 1}
Column 1: {2: 2, 5: 1}
Column 2: {3: 3}
[(1, 2)]
[(2, 2)]
[(3, 3)]

most_common(1) returns a list with one tuple: [(element, count)]. The [0][0] indexing extracts just the element value.

Understanding zip(*matrix)

The asterisk operator unpacks the outer list, passing each row as a separate argument to zip(). This effectively transposes the matrix, turning rows into columns:

matrix = [
[1, 2, 3],
[4, 2, 3],
[1, 5, 3],
]

# These two expressions are equivalent
# zip(*matrix)
# zip([1, 2, 3], [4, 2, 3], [1, 5, 3])

transposed = list(zip(*matrix))
print(transposed)

Output:

[(1, 4, 1), (2, 2, 5), (3, 3, 3)]
tip

Think of zip(*matrix) as rotating the matrix 90 degrees. Rows become columns and columns become rows, which is exactly what you need for column-wise analysis.

Creating a Reusable Function

Wrap the logic in a function with proper error handling:

from collections import Counter

def column_modes(matrix):
"""Find the most common element in each column of a 2D list."""
if not matrix or not matrix[0]:
return []

return [Counter(col).most_common(1)[0][0] for col in zip(*matrix)]

print(column_modes([[1, 2], [1, 3], [2, 2]]))
print(column_modes([[5]]))
print(column_modes([]))

Output:

[1, 2]
[5]
[]

Handling Ties

When multiple values share the highest frequency in a column, Counter.most_common() returns whichever one was encountered first. If you need to identify all tied modes explicitly, check which values match the maximum count:

from collections import Counter

def column_modes_with_ties(matrix):
"""Return all modes for each column when ties exist."""
result = []

for col in zip(*matrix):
counter = Counter(col)
max_count = counter.most_common(1)[0][1]
modes = [val for val, count in counter.items() if count == max_count]
result.append(modes)

return result

matrix = [
[1, 2, 3],
[4, 2, 4],
[1, 5, 3],
]

print(column_modes_with_ties(matrix))

Output:

[[1], [2], [3]]

Column 2 contains [3, 4, 3] and after the tie-aware check, both 3 and 4 are not actually tied. Let us verify with a clearer example:

from collections import Counter

def column_modes_with_ties(matrix):
"""Return all modes for each column when ties exist."""
result = []

for col in zip(*matrix):
counter = Counter(col)
max_count = counter.most_common(1)[0][1]
modes = [val for val, count in counter.items() if count == max_count]
result.append(modes)

return result

matrix = [
[1, 2, 3],
[2, 2, 4],
[1, 5, 3],
[2, 5, 4],
]

print(column_modes_with_ties(matrix))

Output:

[[1, 2], [2, 5], [3, 4]]

Every column has a tie, and all tied values are reported.

Working with Different Data Types

The solution works with any hashable type, including strings, floats, and mixed types:

from collections import Counter

# String matrix
string_matrix = [
["a", "x", "p"],
["b", "x", "q"],
["a", "y", "p"],
]

result = [Counter(col).most_common(1)[0][0] for col in zip(*string_matrix)]
print(result)

# Mixed numeric types
mixed_matrix = [
[1.5, 2, "yes"],
[1.5, 3, "no"],
[2.0, 2, "yes"],
]

result = [Counter(col).most_common(1)[0][0] for col in zip(*mixed_matrix)]
print(result)

Output:

['a', 'x', 'p']
[1.5, 2, 'yes']

Using Pandas for Large Datasets

For extensive data or when working within a data science pipeline, Pandas offers optimized performance and built-in tie handling:

import pandas as pd

matrix = [
[1, 2, 3],
[4, 2, 3],
[1, 5, 3],
]

df = pd.DataFrame(matrix)
modes = df.mode().iloc[0].tolist()

print(modes)

Output:

[1, 2, 3]
note

Pandas mode() returns floats by default. Convert with int() if needed. When ties exist, mode() returns multiple rows, one for each tied value, with NaN filling columns that have fewer modes:

import pandas as pd

matrix = [
[1, 2, 3],
[2, 2, 4],
[1, 5, 3],
[2, 5, 4],
]

df = pd.DataFrame(matrix)
print(df.mode())

Output:

   0  1  2
0 1 2 3
1 2 5 4

Method Comparison

MethodBest ForHandles TiesDependencies
zip() + CounterSmall to medium datasetsReturns first encounteredStandard library
Custom tie-aware functionWhen all modes are neededReturns all tied valuesStandard library
Pandas mode()Large datasets, data pipelinesReturns all tied valuespandas

Conclusion

  • The combination of zip(*matrix) for transposition and Counter.most_common(1) for mode detection provides a clean, efficient solution for finding column-wise modes using only the standard library.
  • When you need to handle ties explicitly, extend the approach by filtering values that match the maximum frequency.
  • For larger datasets or existing data science workflows, Pandas mode() offers optimized performance with built-in tie handling.

Regardless of which method you choose, zip(*matrix) is the key technique that makes column-wise analysis straightforward in Python.