No loops required:

print(a)
[[ 0.93230948         nan  0.47773439  0.76998063]
 [ 0.94460779  0.87882456  0.79615838  0.56282885]
 [ 0.94272934  0.48615268  0.06196785         nan]
 [ 0.64940216  0.74414127         nan         nan]]

#Obtain mean of columns as you need, nanmean is convenient.
col_mean = np.nanmean(a, axis=0)
print(col_mean)
[ 0.86726219  0.7030395   0.44528687  0.66640474]

#Find indices that you need to replace
inds = np.where(np.isnan(a))

#Place column means in the indices. Align the arrays using take
a[inds] = np.take(col_mean, inds[1])

print(a)
[[ 0.93230948  0.7030395   0.47773439  0.76998063]
 [ 0.94460779  0.87882456  0.79615838  0.56282885]
 [ 0.94272934  0.48615268  0.06196785  0.66640474]
 [ 0.64940216  0.74414127  0.44528687  0.66640474]]
Answer from Daniel on Stack Overflow
Top answer
1 of 9
98

No loops required:

print(a)
[[ 0.93230948         nan  0.47773439  0.76998063]
 [ 0.94460779  0.87882456  0.79615838  0.56282885]
 [ 0.94272934  0.48615268  0.06196785         nan]
 [ 0.64940216  0.74414127         nan         nan]]

#Obtain mean of columns as you need, nanmean is convenient.
col_mean = np.nanmean(a, axis=0)
print(col_mean)
[ 0.86726219  0.7030395   0.44528687  0.66640474]

#Find indices that you need to replace
inds = np.where(np.isnan(a))

#Place column means in the indices. Align the arrays using take
a[inds] = np.take(col_mean, inds[1])

print(a)
[[ 0.93230948  0.7030395   0.47773439  0.76998063]
 [ 0.94460779  0.87882456  0.79615838  0.56282885]
 [ 0.94272934  0.48615268  0.06196785  0.66640474]
 [ 0.64940216  0.74414127  0.44528687  0.66640474]]
2 of 9
18

Using masked arrays

The standard way to do this using only numpy would be to use the masked array module.

Scipy is a pretty heavy package which relies on external libraries, so it's worth having a numpy-only method. This borrows from @DonaldHobson's answer.

Edit: np.nanmean is now a numpy function. However, it doesn't handle all-nan columns...

Suppose you have an array a:

>>> a
array([[  0.,  nan,  10.,  nan],
       [  1.,   6.,  nan,  nan],
       [  2.,   7.,  12.,  nan],
       [  3.,   8.,  nan,  nan],
       [ nan,   9.,  14.,  nan]])

>>> import numpy.ma as ma
>>> np.where(np.isnan(a), ma.array(a, mask=np.isnan(a)).mean(axis=0), a)    
array([[  0. ,   7.5,  10. ,   0. ],
       [  1. ,   6. ,  12. ,   0. ],
       [  2. ,   7. ,  12. ,   0. ],
       [  3. ,   8. ,  12. ,   0. ],
       [  1.5,   9. ,  14. ,   0. ]])

Note that the masked array's mean does not need to be the same shape as a, because we're taking advantage of the implicit broadcasting over rows.

Also note how the all-nan column is nicely handled. The mean is zero since you're taking the mean of zero elements. The method using nanmean doesn't handle all-nan columns:

>>> col_mean = np.nanmean(a, axis=0)
/home/praveen/.virtualenvs/numpy3-mkl/lib/python3.4/site-packages/numpy/lib/nanfunctions.py:675: RuntimeWarning: Mean of empty slice
  warnings.warn("Mean of empty slice", RuntimeWarning)
>>> inds = np.where(np.isnan(a))
>>> a[inds] = np.take(col_mean, inds[1])
>>> a
array([[  0. ,   7.5,  10. ,   nan],
       [  1. ,   6. ,  12. ,   nan],
       [  2. ,   7. ,  12. ,   nan],
       [  3. ,   8. ,  12. ,   nan],
       [  1.5,   9. ,  14. ,   nan]])

Explanation

Converting a into a masked array gives you

>>> ma.array(a, mask=np.isnan(a))
masked_array(data =
 [[0.0 --  10.0 --]
  [1.0 6.0 --   --]
  [2.0 7.0 12.0 --]
  [3.0 8.0 --   --]
  [--  9.0 14.0 --]],
             mask =
 [[False  True False  True]
 [False False  True  True]
 [False False False  True]
 [False False  True  True]
 [ True False False  True]],
       fill_value = 1e+20)

And taking the mean over columns gives you the correct answer, normalizing only over the non-masked values:

>>> ma.array(a, mask=np.isnan(a)).mean(axis=0)
masked_array(data = [1.5 7.5 12.0 --],
             mask = [False False False  True],
       fill_value = 1e+20)

Further, note how the mask nicely handles the column which is all-nan!

Finally, np.where does the job of replacement.


Row-wise mean

To replace nan values with row-wise mean instead of column-wise mean requires a tiny change for broadcasting to take effect nicely:

>>> a
array([[  0.,   1.,   2.,   3.,  nan],
       [ nan,   6.,   7.,   8.,   9.],
       [ 10.,  nan,  12.,  nan,  14.],
       [ nan,  nan,  nan,  nan,  nan]])

>>> np.where(np.isnan(a), ma.array(a, mask=np.isnan(a)).mean(axis=1), a)
ValueError: operands could not be broadcast together with shapes (4,5) (4,) (4,5)

>>> np.where(np.isnan(a), ma.array(a, mask=np.isnan(a)).mean(axis=1)[:, np.newaxis], a)
array([[  0. ,   1. ,   2. ,   3. ,   1.5],
       [  7.5,   6. ,   7. ,   8. ,   9. ],
       [ 10. ,  12. ,  12. ,  12. ,  14. ],
       [  0. ,   0. ,   0. ,   0. ,   0. ]])
🌐
Note.nkmk.me
note.nkmk.me › home › python › numpy
NumPy: Replace NaN (np.nan) using np.nan_to_num() and np.isnan() | note.nkmk.me
January 23, 2024 - You can also use np.nanmean() to replace NaN with the mean of the non-missing values.
🌐
w3resource
w3resource.com › python-exercises › numpy › python-numpy-exercise-178.php
Python NumPy: Replace all the nan of a given array with the mean of another array - w3resource
August 29, 2025 - Original arrays: [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19]] [[ 1. 2. nan] [ 4. 5. 6.] [nan 7. nan]] All the nan of array_nums2 replaced by the mean of array_nums1: [[1. 2. 9.5] [4. 5. 6. ] [9.5 7. 9.5]] ... array_nums1 = np.arange(20).reshape(4,5) creates a 1-dimensional NumPy array containing numbers from 0 to 19 and then reshapes it into a 2-dimensional array with 4 rows and 5 columns.
🌐
Data Science Parichay
datascienceparichay.com › home › blog › numpy – replace nan values with mean
Numpy - Replace NaN Values with Mean - Data Science Parichay
July 6, 2022 - Use boolean indexing to replace all instances of NaN in a Numpy array with the mean. Use numpy.isnan() to check for nan values and set such values to the mean value.
🌐
GeeksforGeeks
geeksforgeeks.org › numpy › numpy-replace-nan-values-with-average-of-columns
Replace NaN Values with Average of Columns - NumPy - GeeksforGeeks
September 27, 2025 - This method iterates over NaN positions and replaces them with column averages. Slower than vectorized NumPy approaches but works without NumPy functions. Example: We find NaN positions using np.where, then replace each NaN with the column mean using a loop.
🌐
TutorialsPoint
tutorialspoint.com › article › python-replace-nan-values-with-average-of-columns
Python Replace NaN values with average of columns
The most straightforward approach uses nanmean() to calculate column averages and where() to replace NaN values ? import numpy as np arr = np.array([[1, 2, np.nan], [4, np.nan, 6], [np.nan, 8, 9]]) col_means = np.nanmean(arr, axis=0) arr_filled ...
🌐
IncludeHelp
includehelp.com › python › replace-nan-values-with-average-of-columns-in-numpy-array.aspx
Python - Replace NaN values with average of columns in NumPy array
For this purpose, we will first find the mean of those columns where nan is present and then we will find the indices where nan is present. Finally, we will place the column mean values at the specified indices using arr.take() method. ... # Import numpy import numpy as np # Creating a numpy ...
🌐
Medium
medium.com › @heyamit10 › understanding-numpy-fillna-equivalent-in-numpy-1caafd79145b
Understanding numpy.fillna Equivalent in NumPy | by Hey Amit | Medium
February 8, 2025 - Use numpy.nanmean(data_2d, axis=0) to calculate the mean of each column while ignoring NaN. Use numpy.where to replace NaN with these calculated means.
🌐
Stack Exchange
datascience.stackexchange.com › questions › 61227 › substituting-nan-values-with-mean-code
python - Substituting nan values with mean code - Data Science Stack Exchange
for x in num_cols: imp = SimpleImputer(missing_values=np.nan, strategy='mean') imp.fit(np.array(ds[x]).reshape(-1,1)) ds[x] = imp.transform(np.array(ds[x]).reshape(-1,1))
Find elsewhere
🌐
CASRAI
casrai.org › guides › research tools & software › statistical software for research › numpy nan: how to detect, ignore and replace np.nan
NumPy NaN: Detect, Ignore and Replace np.nan — CASRAI
2 weeks ago - To choose the replacement, pass nan=, for example np.nan_to_num(a, nan=-9999). By default it also replaces positive and negative infinity with very large finite numbers, and it returns a copy unless you pass copy=False (the in-place operation ...
🌐
Reintech
reintech.io › blog › handling-missing-data-nan-values-numpy
Handling Missing Data and NaN Values in NumPy - Reintech
June 13, 2026 - For numerical data, replacing NaN values with the mean, median, or mode often preserves statistical properties better than constant values.
🌐
Medium
medium.com › @heyamit10 › how-to-use-numpy-nanmean-to-calculate-the-mean-while-ignoring-nan-f5da3320be87
How to use numpy.nanmean() to Calculate the Mean While Ignoring nan | by Hey Amit | Medium
February 8, 2025 - You might be wondering: “What makes this function so special?” Well, numpy.nanmean() calculates the mean of an array while completely skipping over nan values. It’s like telling Python, “Hey, just ignore the invalid stuff and give me ...
Top answer
1 of 1
6

One-liner

df.fillna(df.select_dtypes(np.number).mean(), inplace=True)
  • df.select_dtypes(np.number) selects only the numeric columns of the dataframe
  • .mean() computes the mean of each column, returning a new dataframe
  • df.fillna() accepts a dataframe (or other forms) to impute NaNs in named columns
  • inplace just means it happens in the original dataframe itself, without making a copy

You can probably use this to accomplish many more variations of imputation - replacing .mean() with whatever you need.


Update with df from OP

The example dataframe you provided has columns with mixtures of data types. Every column contains strings (e.g. '49' is a string). Only column 2 contains integer types.

When a pandas column contains strings, the column's dtype becomes object. This type is not part of np.number, meaning you cannot select any columns with the method in my one-liner solution above.

Note: OP originally showed a CSV being loaded, which pandas likely loaded into the correct data types. The example snippet for df = pd.DataFrame(...) gives nearly all values as strings. There is a difference then between the original question and the updated snippet.

Solution

I will walk you through the steps required based on your example snippet.

In general, you need to ensure that all your column types are correct. E.g. the Age column should have type int, whereas Region is str. You need to convert your column types.

In [1]: import pandas as pd, numpy as np
In [2]: df = pd.DataFrame({'0': ['Region', 'India','Brazil', 'USA','Brazil','USA','India','Brazil','India','USA','India'],
   ...:                    '1': ['Age', '49', '32', '35','43','45','40','NaN','53','55','42'],
   ...:                    '2': ['Income', 86400, 57600, 64800,73200,'NaN',69600,62400,94800,99600,80400],
   ...:                    '3': ['Online Shopper','No','Yes',' No',' No','Yes','Yes','No','Yes','No','Yes']},
   ...:                   index=['0', '1', '2', '3','4','5','6','7','8','9','10'])
   ...:
In [3]: df
Out[3]:
         0    1       2               3
0   Region  Age  Income  Online Shopper
1    India   49   86400              No
2   Brazil   32   57600             Yes
3      USA   35   64800              No
4   Brazil   43   73200              No
5      USA   45     NaN             Yes
6    India   40   69600             Yes
7   Brazil  NaN   62400              No
8    India   53   94800             Yes
9      USA   55   99600              No
10   India   42   80400             Yes

In [4]: df.dtypes
Out[4]:
0    object
1    object
2    object
3    object
dtype: object

Column names stored as the first row, so make them actual column names and remove that first row:

In [5]: column_names = df.iloc[0].tolist()
In [6]: df = df.iloc[1:]
In [7]: df.columns = column_names

The missing NaN values are stored as string: replace with numpy.nan:

In [8]: df[df == "NaN"] = np.nan

Convert the types of all columns, using a column names to type mapping - not all object. Note that np.nan is actually a float - so we can't use int:

In [9]: df = df.astype({"Region": str, "Age": float, "Income": float, "Online Shopper": bool})
In [10]: df
Out[10]:
    Region   Age   Income  Online Shopper
1    India  49.0  86400.0            True
2   Brazil  32.0  57600.0            True
3      USA  35.0  64800.0            True
4   Brazil  43.0  73200.0            True
5      USA  45.0      NaN            True
6    India  40.0  69600.0            True
7   Brazil   NaN  62400.0            True
8    India  53.0  94800.0            True
9      USA  55.0  99600.0            True
10   India  42.0  80400.0            True

In [11]: df.dtypes
Out[11]:
Region             object
Age               float64
Income            float64
Online Shopper       bool
dtype: object

The one-liner solution now works:

In [12]: imputed_df = df.fillna(df.select_dtypes(np.number).mean())
In [13]: imputed_df
Out[13]:
    Region        Age        Income  Online Shopper
1    India  49.000000  86400.000000            True
2   Brazil  32.000000  57600.000000            True
3      USA  35.000000  64800.000000            True
4   Brazil  43.000000  73200.000000            True
5      USA  45.000000  76533.333333            True
6    India  40.000000  69600.000000            True
7   Brazil  43.777778  62400.000000            True
8    India  53.000000  94800.000000            True
9      USA  55.000000  99600.000000            True
10   India  42.000000  80400.000000            True

You might want to convert the columns to new types, e.g. making Age of type int. I will leave this is an exercise for you. I think this shows you many of the tools you will need to work it out.

🌐
GeeksforGeeks
geeksforgeeks.org › how-to-fill-nan-values-with-mean-in-pandas
How to fill NAN values with mean in Pandas? - GeeksforGeeks
March 21, 2024 - The code focuses on the second column (index 1) denoted as 'X.' The SimpleImputer from scikit-learn is employed to replace missing values (NaN) in 'X' with the mean of the available values.
🌐
Spark Code Hub
sparkcodehub.com › numpy › data analysis › handling nan values
Mastering Handling NaN Values with NumPy Arrays
Each concept is explained in depth to ensure clarity, with relevant internal links to enhance understanding, maintaining a logical and cohesive narrative as of June 2, 2025. ... np.nan is a special floating-point value defined by the IEEE 754 standard, used to represent missing, undefined, or invalid data. Unlike ... np.nan values is critical for data preprocessing, ensuring accurate statistical analysis, machine learning, and scientific computing. NumPy provides tools to detect, filter, replace, or ignore
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.nan_to_num.html
numpy.nan_to_num — NumPy v2.5 Manual
Replace NaN with zero and infinity with large finite numbers (default behaviour) or with the numbers defined by the user using the nan, posinf and/or neginf keywords.