Question: There are a lot of posts about how to count `NaN` in a list or pandas series, as well as the time efficiency of the various options. One solution I have not seen is self equality: If `y == np.nan` then `(y != y) is True`. So a quick way to count the NaNs in a list would be:

```py
import pandas as pd
import numpy as np

lst = pd.Series([np.nan, 5, 4, 3, 2, np.nan])
count = sum(1 for x in lst if x != x)

```

I hadn't seen that solution before, which makes me wonder: When will this fail to work the way I want it to work (eg maybe for dtypes that aren't in my columns - I have floats and strings? I've done some testing with my own data, and found this solution to be equivalent to:

```py
count = lst.isnull().sum()
# and
count = len([x for x in lst if x != x])

```

I've found that the speed is in this order from fastest to slowest: `sum`, `len`, `.sum()`

Answer: 
You could use [`numpy.isnan()`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.isnan.html), so your code would look like:

```py
import pandas as pd
import numpy as np

lst = pd.Series([np.nan, 5, 4, 3, 2, np.nan])
count = len([x for x in lst if np.isnan(x)])

```

But if you want to be fancy:

```py
count = sum(np.isnan(lst))

```

Or if you're concerned about memory:

```py
# Less elegant, but does the job
count = 0
for x in lst:
    if np.isnan(x):
        count += 1

```
Based on the comments, I did a basic time complexity experiment between the two proposed methods. Note that this doesn't address any cases where your approach fails, as I didn't find any.

Setup:

```
def make_col():
    return [np.random.choice([1,2,3,4,5,6,7,8,9,10,np.NaN]) for i in range(100000)]

df=pd.DataFrame({k:make_ops() for k in list('abcdefghijklmnopqrstuvwxyz')})

df.shape
(100000, 26)

```

Experiment (note that I tried to be explicit at each step rather than optimizing for total efficiency, to show the difference between the two):

```
%%timeit
n=[]
for col in df.columns:
    count=len([x for x in df[col] if x != x])
    n.append(count)
134 ms

%%timeit
n=[]
for col in df.columns:
    count=count = df[col].isnull().sum()
    n.append(count)
29.7 ms

```

For approximately 80% improvement using the built-in methods

Interestingly, based on the numpy/Cython/C optimization under the hood, if you run the `%%prun` profiler on both of the above operations, you get fewer individual function calls with your approach, but more overall runtime.
Respectively:

```
2013 function calls in 0.248 seconds

```

vs. 

```
8903 function calls in 0.046 seconds

```

And, regarding memory usage between the two, it looks like you're right regarding the two being logically equivalent regarding copying and storage. Both snippets above come in around 

`peak memory: 247.20 MiB`

using the `%%memit` magic from [this source](https://ipython-books.github.io/44-profiling-the-memory-usage-of-your-code-with-memory_profiler/) (which as a caveat, I haven't seen or used previously and am unaware of any gotchas or issues regarding its use)