Pandas acessing rows with nan - python

I was working on a dataframe like this.
df = pd.DataFrame([[1, np.nan, 2],
[2, 3, 5],
[np.nan, 4, 6]],index=['a','b','c'])
df
0 1 2
a 1.0 NaN 2
b 2.0 3.0 5
c NaN 4.0 6
When I use df.isnull() it gives the output as :
0 1 2
a False True False
b False False False
c True False False
When I use df[df.isnull()] why does it show all elements as nan:
df[df.isnull()]
0 1 2
a NaN NaN NaN
b NaN NaN NaN
c NaN NaN NaN
Can somebody explain why it is happening?

This is mask for the dataframe , it will mask all False value to np.nan.
For example
df[~df.isnull()]
Out[342]:
0 1 2
a 1.0 NaN 2
b 2.0 3.0 5
c NaN 4.0 6
and
df[df==2]
Out[343]:
0 1 2
a NaN NaN 2.0
b 2.0 NaN NaN
c NaN NaN NaN
Since isnull return all np.nan value as True
After mask
df[df.isnull()]
Out[344]:
0 1 2
a NaN(False mask as NaN) NaN(True) NaN
b NaN(True) NaN NaN
c NaN NaN NaN

Related

How do you shift each row in pandas data frame by a specific value?

If I have a pandas dataframe like this:
2 3 4 NaN NaN NaN
1 NaN NaN NaN NaN NaN
5 6 7 2 3 NaN
4 3 NaN NaN NaN NaN
and an array for the number I would like to shift:
array = [2, 4, 0, 3]
How do I iterate through each row to shift the columns by the number in my array to get something like this:
NaN NaN 2 3 4 NaN
NaN NaN NaN NaN 1 NaN
5 6 7 2 3 NaN
NaN NaN NaN 3 4 NaN
I was trying to do something like this but had no luck.
df = pd.DataFrame(values)
for rows in df.iterrows():
df[rows] = df.shift[change_in_bins[rows]]
Use for loop with loc and shift:
for index,value in enumerate([2, 4, 0, 3]):
df.loc[index,:] = df.loc[index,:].shift(value)
print(df)
0 1 2 3 4 5
0 NaN NaN 2.0 3.0 4.0 NaN
1 NaN NaN NaN NaN 1.0 NaN
2 5.0 6.0 7.0 2.0 3.0 NaN
3 NaN NaN NaN 4.0 3.0 NaN

Fill missing value by averaging previous row value

I want to fill missing value with the average of previous N row value, example is shown below:
N=2
df = pd.DataFrame([[np.nan, 2, np.nan, 0],
[3, 4, np.nan, 1],
[np.nan, np.nan, np.nan, 5],
[np.nan, 3, np.nan, np.nan]],
columns=list('ABCD'))
DataFrame is like:
A B C D
0 NaN 2.0 NaN 0
1 3.0 4.0 NaN 1
2 NaN NaN NaN 5
3 NaN 3.0 NaN NaN
Result should be:
A B C D
0 NaN 2.0 NaN 0
1 3.0 4.0 NaN 1
2 NaN (4+2)/2 NaN 5
3 NaN 3.0 NaN (1+5)/2
I am wondering if there is elegant and fast way to achieve this without for loop.
rolling + mean + shift
You will need to modify the below logic to interpret the mean of NaN and another value, in the case where one of the previous two values are null.
df = df.fillna(df.rolling(2).mean().shift())
print(df)
A B C D
0 NaN 2.0 NaN 0.0
1 3.0 4.0 NaN 1.0
2 NaN 3.0 NaN 5.0
3 NaN 3.0 NaN 3.0

binary operation between rows in DataFrame

Original Dataframe as below,
s1 = pd.DataFrame([1,'a',np.nan,np.nan,np.nan,2,'b',np.nan,np.nan,np.nan,3,'c',np.nan,np.nan,np.nan]).T
In [37]: s1
Out[37]:
1 a NaN NaN NaN 2 b NaN NaN NaN 3 c NaN NaN NaN
Desired DataFrame
Nan 1 NaN NaN NaN Nan 2 NaN NaN NaN Nan 3 NaN NaN NaN
Nan a NaN NaN NaN Nan b NaN NaN NaN Nan c NaN NaN NaN
My solution:
s2 =s1.shift(periods=1,axis=1)
s=pd.concat([s2,s1],axis='index',join='inner',ignore_index=True,copy=False)
print(s)
Nan 1 a NaN NaN NaN 2 b NaN NaN NaN 3 c NaN NaN NaN
1 a NaN NaN NaN 2 b NaN NaN NaN 3 c NaN NaN NaN
Then, how can I give each column value of NaN except that 2 rows in that column are all non-NaN? I wasted 2 hours on this small issue trying to come up a pythonic way to do it except if/else/for loop.
last step will be,
s.fillna(method='ffill',axis=1,inplace=True)
Thanks in advance
You can create mask for columns with any NaNs values and then set NaNs by loc:
s2 = s1.shift(periods=1,axis=1)
#added ignore_index=True for default unique index
s = pd.concat([s2,s1], axis='index', ignore_index=True)
m = s.isnull().any()
#alternative
#m = ~s.notnull().all()
s.loc[:, m] = np.nan
print(s)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
0 NaN 1 NaN NaN NaN NaN 2 NaN NaN NaN NaN 3 NaN NaN NaN
1 NaN a NaN NaN NaN NaN b NaN NaN NaN NaN c NaN NaN NaN
Detail:
print(s.isnull())
0 1 2 3 4 5 6 7 8 9 10 11 \
0 True False True True True True False True True True True False
1 True False True True True True False True True True True False
12 13 14
0 True True True
1 True True True
print(m)
0 True
1 False
2 True
3 True
4 True
5 True
6 False
7 True
8 True
9 True
10 True
11 False
12 True
13 True
14 True
dtype: bool

Pandas dataframe assign 0 with all the value after first non-nan value

I have a dataframe with several columns and a series whose value are the index of the first non-nan value of the dataframe:
dataframe x:
a b c d e f g h
1 nan nan 2 nan nan nan nan nan
2 nan 2 nan 10 23 nan nan nan
3 3 nan 23 42 232 3 nan 5
series y:
a 3
b 2
c 1
d 2
e 2
f 3
g nan
h 3
now I want to assign 0 with all the value after first non-nan value(include the first non-nan value) by the value of series(whose value are the index of the first non-nan value of the dataframe x)
result is
a b c d e f g h
1 nan nan 0 nan nan nan nan nan
2 nan 0 0 0 0 nan nan nan
3 0 0 0 0 0 0 nan 0
I use applymap to deal with it,but applymap seems not to deal with index information,here is my code:
def mycode(x,y)
if x.index<=Y:
return 0
else:
return x
cal = x.applymap(lambda x: mycode(x,y))
You can use ffill (fillna with method='ffill') with clip:
x = x.ffill().clip(0,0)
Alternative solutions with boolean mask and where or
mask:
x = x.where(x.ffill().isnull(), 0)
x = x.mask(x.ffill().notnull(), 0)
print (x)
a b c d e f g h
1 NaN NaN 0.0 NaN NaN NaN NaN NaN
2 NaN 0.0 0.0 0.0 0.0 NaN NaN NaN
3 0.0 0.0 0.0 0.0 0.0 0.0 NaN 0.0
Detail:
print (x.ffill())
a b c d e f g h
1 NaN NaN 2.0 NaN NaN NaN NaN NaN
2 NaN 2.0 2.0 10.0 23.0 NaN NaN NaN
3 3.0 2.0 23.0 42.0 232.0 3.0 NaN 5.0
print (x.ffill().isnull())
a b c d e f g h
1 True True False True True True True True
2 True False False False False True True True
3 False False False False False False True False

pandas DataFrame filter by rows and columns

I have a python pandas DataFrame that looks like this:
A B C ... ZZ
2008-01-01 00 NaN NaN NaN ... 1
2008-01-02 00 NaN NaN NaN ... NaN
2008-01-03 00 NaN NaN 1 ... NaN
... ... ... ... ... ...
2012-12-31 00 NaN 1 NaN ... NaN
and I can't figure out how to get a subset of the DataFrame where there is one or more '1' in it, so that the final df should be something like this:
B C ... ZZ
2008-01-01 00 NaN NaN ... 1
2008-01-03 00 NaN 1 ... NaN
... ... ... ... ...
2012-12-31 00 1 NaN ... NaN
This is, removing all rows and columns that do not have a 1 in it.
I try this which seems to remove the rows with no 1:
df_filtered = df[df.sum(1)>0]
And the try to remove columns with:
df_filtered = df_filtered[df.sum(0)>0]
but get this error after the second line:
IndexingError('Unalignable boolean Series key provided')
Do it with loc:
In [90]: df
Out[90]:
0 1 2 3 4 5
0 1 NaN NaN 1 1 NaN
1 NaN NaN NaN NaN NaN NaN
2 1 1 NaN NaN 1 NaN
3 1 NaN 1 1 NaN NaN
4 NaN NaN NaN NaN NaN NaN
In [91]: df.loc[df.sum(1) > 0, df.sum(0) > 0]
Out[91]:
0 1 2 3 4
0 1 NaN NaN 1 1
2 1 1 NaN NaN 1
3 1 NaN 1 1 NaN
Here's why you get that error:
Let's say I have the following frame, df, (similar to yours):
In [112]: df
Out[112]:
a b c d e
0 0 1 1 NaN 1
1 NaN NaN NaN NaN NaN
2 0 0 0 NaN 0
3 0 0 1 NaN 1
4 1 1 1 NaN 1
5 0 0 0 NaN 0
6 1 0 1 NaN 0
When I sum along the rows and threshold at 0, I get:
In [113]: row_sum = df.sum()
In [114]: row_sum > 0
Out[114]:
a True
b True
c True
d False
e True
dtype: bool
Since the index of row_sum is the columns of df, it doesn't make sense in this case to try to use the values of row_sum > 0 to fancy-index into the rows of df, since their row indices are not aligned and they cannot be aligned.
Alternatively to remove all NaN rows or columns you can use .any() too.
In [1680]: df
Out[1680]:
0 1 2 3 4 5
0 1.0 NaN NaN 1.0 1.0 NaN
1 NaN NaN NaN NaN NaN NaN
2 1.0 1.0 NaN NaN 1.0 NaN
3 1.0 NaN 1.0 1.0 NaN NaN
4 NaN NaN NaN NaN NaN NaN
In [1681]: df.loc[df.any(axis=1), df.any(axis=0)]
Out[1681]:
0 1 2 3 4
0 1.0 NaN NaN 1.0 1.0
2 1.0 1.0 NaN NaN 1.0
3 1.0 NaN 1.0 1.0 NaN

Categories

Resources