Following fmarc's answer to How to replace all non-NaN entries of a dataframe with 1 and all NaN with 0, we can convert the dataframe to contain only 0s and 1s.
df = df.notnull().astype('int')
Then I replace the 0s and 1s in the column 'testA' with 'not A' and 'A'. I repeat the similar thing for the column 'testB'.
df['testA'].replace(1, 'A', inplace=True)
df['testA'].replace(0, 'not A', inplace=True)
df['testB'].replace(1, 'B', inplace=True)
df['testB'].replace(0, 'not B', inplace=True)
I do this to simplify our next step, which is to add the two strings in 'testA' and 'testB' and get their value_counts:
df['sum'] = df['testA'] + ' + ' +newdf['testB']
df['sum'].value_counts()
The last line of code should yield your desired result. Here's what I got:
Input:
id testA testB
0 1 3.0 NaN
1 1 1.0 3.0
2 2 2.0 NaN
3 2 NaN 1.0
4 2 0.0 0.0
5 3 NaN NaN
6 3 1.0 1.0
Output:
A + B 3
A + not B 2
not A + B 1
not A + not B 1
Name: sum, dtype: int64