This question already has answers here:
Filter out rows with more than certain number of NaN
(3 answers)
Closed 4 years ago.
I am trying to remove the rows in the data frame with more than 7 null values. Please suggest something that is efficient to achieve this.
If I understand correctly, you need to remove rows only if total nan's in a row is more than 7:
df = df[df.isnull().sum(axis=1) < 7]
This will keep only rows which have nan's less than 7 in the dataframe, and will remove all having nan's > 7.
dropna has a thresh argument. Subtract your desired number from the number of columns.
thresh : int, optional Require that many non-NA values.
df.dropna(thresh=df.shape[1]-7, axis=0)
Sample Data:
print(df)
0 1 2 3 4 5 6 7
0 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN 5.0
2 6.0 7.0 8.0 9.0 NaN NaN NaN NaN
3 NaN NaN 11.0 12.0 13.0 14.0 15.0 16.0
df.dropna(thresh=df.shape[1]-7, axis=0)
0 1 2 3 4 5 6 7
1 NaN NaN NaN NaN NaN NaN NaN 5.0
2 6.0 7.0 8.0 9.0 NaN NaN NaN NaN
3 NaN NaN 11.0 12.0 13.0 14.0 15.0 16.0
Related
I am hoping someone can help me optimize the following Python/Pandas code. My code works, but I know there must be a cleaner and faster way to perform the operation under consideration.
I am looking for an optimized strategy because my use case will involve 16 unique ADC Types, as opposed to 4 in the example below. Also, my initial Pandas Series (i.e. ADC Type column), will be several 100,000 data points in length, rather than 8 in the example below.
import numpy as np
import pandas as pd
from enum import Enum
data_dict = {"RAW": [4000076160, 5354368, 4641792, 4289860736,
4136386944, 5440384, 4772864, 4289881216],
"ADC_TYPE": [3, 7, 8, 9,
3, 7, 8, 9]}
df = pd.DataFrame(data_dict)
print(df)
The initial DataFrame (i.e. df) is:
RAW ADC_TYPE
0 4000076160 3
1 5354368 7
2 4641792 8
3 4289860736 9
4 4136386944 3
5 5440384 7
6 4772864 8
7 4289881216 9
I then manipulate the DataFrame above using the following code:
unique_types = df["ADC_TYPE"].unique()
dict_concat = {"RAW": [],
"ADC_TYPE_3": [],
"ADC_TYPE_7": [],
"ADC_TYPE_8": [],
"ADC_TYPE_9": []}
df_concat = pd.DataFrame(dict_concat)
for adc_type in unique_types:
df_group = df.groupby(["ADC_TYPE"]).get_group(adc_type).rename(columns={"ADC_TYPE": f"ADC_TYPE_{adc_type}"})
df_concat = pd.concat([df_concat, df_group])
print(df_concat.sort_index())
The returned DataFrame (i.e. df_concat) is displayed below. The ordering of RAW and the associated ADC Type values must remain unchanged. I need the return DataFrame to look just like the DataFrame below.
RAW ADC_TYPE_3 ADC_TYPE_7 ADC_TYPE_8 ADC_TYPE_9
0 4.000076e+09 3.0 NaN NaN NaN
1 5.354368e+06 NaN 7.0 NaN NaN
2 4.641792e+06 NaN NaN 8.0 NaN
3 4.289861e+09 NaN NaN NaN 9.0
4 4.136387e+09 3.0 NaN NaN NaN
5 5.440384e+06 NaN 7.0 NaN NaN
6 4.772864e+06 NaN NaN 8.0 NaN
7 4.289881e+09 NaN NaN NaN 9.0
This is just a pivot table with a prefix.
Edit: To preserve sorting, you can reindex from the original dataframe
df = pd.DataFrame({'RAW': {0: 4000076160,
1: 5354368,
2: 4641792,
3: 4289860736,
4: 4136386944,
5: 5440384,
6: 4772864,
7: 4289881216},
'ADC_TYPE': {0: 3, 1: 7, 2: 8, 3: 9, 4: 3, 5: 7, 6: 8, 7: 9}})
out = df.pivot(index='RAW', columns = 'ADC_TYPE', values='ADC_TYPE').add_prefix('ACC_TYPE_').reset_index().rename_axis(None, axis=1)
out = out.set_index('RAW').reindex(df['RAW']).reset_index()
Output
RAW ACC_TYPE_3 ACC_TYPE_7 ACC_TYPE_8 ACC_TYPE_9
0 4000076160 3.0 NaN NaN NaN
1 5354368 NaN 7.0 NaN NaN
2 4641792 NaN NaN 8.0 NaN
3 4289860736 NaN NaN NaN 9.0
4 4136386944 3.0 NaN NaN NaN
5 5440384 NaN 7.0 NaN NaN
6 4772864 NaN NaN 8.0 NaN
7 4289881216 NaN NaN NaN 9.0
Here is a way using str.get_dummies()
df2 = df.set_index('RAW')['ADC_TYPE'].astype(str).str.get_dummies()
(df2.mul(pd.to_numeric(df2.columns),axis=1)
.mask(lambda x: x.eq(0))
.rename('ADC_TYPE_{}'.format,axis=1)
.reset_index())
Here is a slightly different way using pd.get_dummies()
df2 = pd.get_dummies(df.set_index('RAW'),columns = ['ADC_TYPE'])
df2.mul((df2.columns.str.split('_').str[-1]).astype(int)).where(lambda x: x.ne(0))
You can also use set_index() and unstack()
(df.set_index(['RAW',df['ADC_TYPE'].astype(str).map('ADC_TYPE_{}'.format)])['ADC_TYPE']
.unstack().reindex(df['RAW']).reset_index())
Output:
RAW ADC_TYPE_3 ADC_TYPE_7 ADC_TYPE_8 ADC_TYPE_9
0 4000076160 3.0 NaN NaN NaN
1 5354368 NaN 7.0 NaN NaN
2 4641792 NaN NaN 8.0 NaN
3 4289860736 NaN NaN NaN 9.0
4 4136386944 3.0 NaN NaN NaN
5 5440384 NaN 7.0 NaN NaN
6 4772864 NaN NaN 8.0 NaN
7 4289881216 NaN NaN NaN 9.0
I liked the idea of using get_dummies, so I modified it a bit:
df = (pd.get_dummies(df, 'ADC_TYPE', '_', columns=['ADC_TYPE'])
.replace(1, np.nan)
.apply(lambda x: x.fillna(df['ADC_TYPE']))
.replace(0, np.nan))
Output:
RAW ADC_TYPE_3 ADC_TYPE_7 ADC_TYPE_8 ADC_TYPE_9
0 4000076160 3.0 NaN NaN NaN
1 5354368 NaN 7.0 NaN NaN
2 4641792 NaN NaN 8.0 NaN
3 4289860736 NaN NaN NaN 9.0
4 4136386944 3.0 NaN NaN NaN
5 5440384 NaN 7.0 NaN NaN
6 4772864 NaN NaN 8.0 NaN
7 4289881216 NaN NaN NaN 9.0
Using crosstab:
out = pd.crosstab(
df["RAW"], df["ADC_TYPE"], values=df["ADC_TYPE"], aggfunc="first"
).rename_axis(None, axis=1)
out.columns = out.columns.map("ADC_TYPE_{}".format)
out = out.reindex(df["RAW"]).reset_index()
print(out):
RAW ADC_TYPE_3 ADC_TYPE_7 ADC_TYPE_8 ADC_TYPE_9
0 4000076160 3.0 NaN NaN NaN
1 5354368 NaN 7.0 NaN NaN
2 4641792 NaN NaN 8.0 NaN
3 4289860736 NaN NaN NaN 9.0
4 4136386944 3.0 NaN NaN NaN
5 5440384 NaN 7.0 NaN NaN
6 4772864 NaN NaN 8.0 NaN
7 4289881216 NaN NaN NaN 9.0
What is the most pandastic way to forward fill with ascending logic (without iterating over the rows)?
input:
import pandas as pd
import numpy as np
df = pd.DataFrame()
df['test'] = np.nan,np.nan,1,np.nan,np.nan,3,np.nan,np.nan,2,np.nan,6,np.nan,np.nan
df['desired_output'] = np.nan,np.nan,1,1,1,3,3,3,3,3,6,6,6
print (df)
output:
test desired_output
0 NaN NaN
1 NaN NaN
2 1.0 1.0
3 NaN 1.0
4 NaN 1.0
5 3.0 3.0
6 NaN 3.0
7 NaN 3.0
8 2.0 3.0
9 NaN 3.0
10 6.0 6.0
11 NaN 6.0
12 NaN 6.0
In the 'test' column, the number of consecutive NaN's is random.
In the 'desired_output' column, trying to forward fill with ascending values only. Also, when lower values are encountered (row 8, value = 2.0 above), they are overwritten with the current higher value.
Can anyone help? Thanks in advance.
You can combine cummax to select the cumulative maximum value and ffill to replace the NaNs:
df['desired_output'] = df['test'].cummax().ffill()
output:
test desired_output
0 NaN NaN
1 NaN NaN
2 1.0 1.0
3 NaN 1.0
4 NaN 1.0
5 3.0 3.0
6 NaN 3.0
7 NaN 3.0
8 2.0 3.0
9 NaN 3.0
10 6.0 6.0
11 NaN 6.0
12 NaN 6.0
intermediate Series:
df['test'].cummax()
0 NaN
1 NaN
2 1.0
3 NaN
4 NaN
5 3.0
6 NaN
7 NaN
8 3.0
9 NaN
10 6.0
11 NaN
12 NaN
Name: test, dtype: float64
I want to turn a DataFrame (or a numpy array):
df1:
0 1 2
0 1. 5. 9.
1 2. 6. 10.
2 3. 7. 11.
3 4. 8. 12.
into a DataFrame like this:
df1
0 1 2 3 4 5 6
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN 1. NaN 5. NaN 9. NaN
2 NaN NaN NaN NaN NaN NaN NaN
3 NaN 2. NaN 6. NaN 10. NaN
4 NaN NaN NaN NaN NaN NaN NaN
5 NaN 3. NaN 7. NaN 11. NaN
6 NaN NaN NaN NaN NaN NaN NaN
7 NaN 4. NaN 8. NaN 12. NaN
8 NaN NaN NaN NaN NaN NaN NaN
, i.e., I want to insert NaN rows and columns on df1 (as many as I want)
Could you make this work even for a large DataFrame, where you cannot do this manually?
So far, I have this:
import numpy as np
import pandas as pd
p = np.arange(1,13).reshape(4,3)
p1 = pd.DataFrame(p)
#Add a row of NaN's on p1
p1.index = range(1, 2*len(p1)+1, 2)
p1 = p1.reindex(index=range(2*len(p1)))
#Repeat for rows...I know its a lil bit st*pid
p1 = pd.DataFrame(p1)
p1.index = range(1, 2*len(p1)+1, 2)
p1 = p1.reindex(index=range(2*len(p1)))
#etc...
p1 = pd.DataFrame(p1)
p1.index = range(1, 2*len(p1)+1, 2)
p1 = p1.reindex(index=range(2*len(p1)))
It seems to work, but only for rows until now...
e.g., see this
Based on this answer you can interleave two dataframes on a particular axis.
pd.concat([df1, df2]).sort_index().reset_index(drop=True)
You can start by interleaving by rows (axis=0) df1 with a dataframe containing nan values. And do the same on the columns (axis=1) with another dataframe of nan values.
df1 = pd.DataFrame([[1., 5., 9.], [2., 6., 10.], [3., 7., 11.], [4., 8., 12.]])
rows, cols = df1.shape
Tricky part is getting the sizes right:
nan1 = pd.DataFrame([[np.nan]*cols]*(rows+1))
nan2 = pd.DataFrame([[np.nan]*(cols + 1)]*(2*rows + 1))
Then perform two consecutives concatenations, on axis=0 (default one) and axis=1:
df2_r = pd.concat([nan1, df1]).sort_index().reset_index(drop=True)
df2 = pd.concat([nan2, df2_r], axis=1).sort_index(axis=1).T.reset_index(drop=True).T
Edit: it seems there's is no built-in method to reset the columns indexing. However this will do:
df.T.reset_index(drop=True).T
Here are the results for each operation:
df1
0 1 2
0 1.0 5.0 9.0
1 2.0 6.0 10.0
2 3.0 7.0 11.0
3 4.0 8.0 12.0
nan1
0 1 2
0 NaN NaN NaN
1 NaN NaN NaN
2 NaN NaN NaN
3 NaN NaN NaN
4 NaN NaN NaN
concat on axis=0
0 1 2
0 NaN NaN NaN
1 1.0 5.0 9.0
2 NaN NaN NaN
3 2.0 6.0 10.0
4 NaN NaN NaN
5 3.0 7.0 11.0
6 NaN NaN NaN
7 4.0 8.0 12.0
8 NaN NaN NaN
nan2
0 1 2 3
0 NaN NaN NaN NaN
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN NaN NaN NaN
4 NaN NaN NaN NaN
5 NaN NaN NaN NaN
6 NaN NaN NaN NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
concat on axis=1
0 1 2 3 4 5 6
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN 1.0 NaN 5.0 NaN 9.0 NaN
2 NaN NaN NaN NaN NaN NaN NaN
3 NaN 2.0 NaN 6.0 NaN 10.0 NaN
4 NaN NaN NaN NaN NaN NaN NaN
5 NaN 3.0 NaN 7.0 NaN 11.0 NaN
6 NaN NaN NaN NaN NaN NaN NaN
7 NaN 4.0 NaN 8.0 NaN 12.0 NaN
8 NaN NaN NaN NaN NaN NaN NaN
I am curious to see what you have tried so far, but here is an easy "quick and dirty" way to do it for your example. This is not a definitive answer: I'll let you figure out how to generalize it to any dataframe sizes/content you might have.
I am providing this code for your example so you have an idea which pandas functions/properties to use.
import pandas as pd
import numpy as np
# Making your base DataFrame
df = pd.DataFrame([[1,5,9], [2,6,8], [3,7,4]])
df:
0 1 2
0 1 5 9
1 2 6 8
2 3 7 4
spacing out your columns existing columns numbers and adding filling the left columns numbers with NaN:
df.columns = [1,3,5]
for i in range(0, 8, 2):
df[i] = np.NaN
df:
1 3 5 0 2 4 6
0 1 5 9 NaN NaN NaN NaN
1 2 6 8 NaN NaN NaN NaN
2 3 7 4 NaN NaN NaN NaN
Now adding extra rows, with NaN data (we need 4 more with 7 columns)
df2 = pd.DataFrame([[np.NaN] * 7] * 4)
df = pd.concat([df, df2])
df3:
0 1 2 3 4 5 6
0 NaN 1.0 NaN 5.0 NaN 9.0 NaN
1 NaN 2.0 NaN 6.0 NaN 8.0 NaN
2 NaN 3.0 NaN 7.0 NaN 4.0 NaN
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN
As you can see: we have the right data, and it is now only a matter of ordering your rows.
df3.index = [1,3,5,0,2,4,6]
df3 = df3.sort_index()
df3:
0 1 2 3 4 5 6
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN 1.0 NaN 5.0 NaN 9.0 NaN
2 NaN NaN NaN NaN NaN NaN NaN
3 NaN 2.0 NaN 6.0 NaN 8.0 NaN
4 NaN NaN NaN NaN NaN NaN NaN
5 NaN 3.0 NaN 7.0 NaN 4.0 NaN
6 NaN NaN NaN NaN NaN NaN NaN
I think this is a very elegant way to solve this.
array=np.array([[1,5,9],[2,6,10],[3,7,11],[4,8,12]])
Data=pd.DataFrame(array)
Data.index=Data.index*2+1
Data.columns=Data.columns*2+1
Data=Data.reindex(list(range(0,9)))
Data=Data.T.reindex(list(range(0,9)))
A fast way using numpy (work with dataframe as well):
# Sample data
a = np.arange(1,13).reshape(4,3)
df = pd.DataFrame(a)
# New data with empty values
a2 = np.empty([i*2+1 for i in a.shape])
a2[:] = np.nan
a2[1::2, 1::2] = a
Output of pd.DataFrame(a2):
0 1 2 3 4 5 6
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN 1.0 NaN 2.0 NaN 3.0 NaN
2 NaN NaN NaN NaN NaN NaN NaN
3 NaN 4.0 NaN 5.0 NaN 6.0 NaN
4 NaN NaN NaN NaN NaN NaN NaN
5 NaN 7.0 NaN 8.0 NaN 9.0 NaN
6 NaN NaN NaN NaN NaN NaN NaN
7 NaN 10.0 NaN 11.0 NaN 12.0 NaN
8 NaN NaN NaN NaN NaN NaN NaN
Note: If you have a DataFrame, just replace a.shape by df.shape, and a by df.values.
I want to fill e column's NaN with its most closest (by position from left side) not NaN columns' values.
a b c d e
0 1 2.0 3.0 6.0 3.0
1 3 5.0 7.0 NaN NaN
2 2 4.0 NaN NaN NaN
3 5 6.0 NaN NaN NaN
4 3 NaN NaN NaN NaN
For example, for the second row of e, its most closest Not NaN column is e by position, then we take 7.0, is it possible to do this in Pandas? Thanks.
The expected output is like this:
a b c d e
0 1 2.0 3.0 6.0 3.0
1 3 5.0 7.0 NaN 7.0
2 2 4.0 NaN NaN 4.0
3 5 6.0 NaN NaN 6.0
4 3 NaN NaN NaN 3.0
If answer should be simplify get all first non missing values from left side to last column use forward filling them and select last column by position:
df.e = df.ffill(axis=1).iloc[:, -1]
I have a long list of columns and I want to subtract the previous column from the current column and replace the current column with the difference.
So if I have:
A B C D
1 NaN 3 7
3 NaN 8 10
2 NaN 6 11
I want the output to be:
A B C D
1 NaN 2 4
3 NaN 5 2
2 NaN 4 5
I have been trying to use this code:
df2 = df1.diff(axis=1)
but this does not produce the desired output
Thanks in advance.
You can do this with df.where and then update to bring back the first non-null entry for each row of your DataFrame.
Sample Data: df
A B C D
0 1.0 NaN 3.0 7.0
1 1.0 4.0 5.0 9.0
2 NaN 4.0 NaN 4.0
3 NaN 4.0 NaN NaN
4 NaN NaN 3.0 7.0
5 3.0 NaN NaN 7.0
6 6.0 NaN NaN NaN
Code:
df_d = df.where(df.isnull(),
df.fillna(method='ffill', axis=1).diff(axis=1))
df_d.update(df.where(df.notnull().cumsum(1).cumsum(1) == 1))
Output: df_d
A B C D
0 1.0 NaN 2.0 4.0
1 1.0 3.0 1.0 4.0
2 NaN 4.0 NaN 0.0
3 NaN 4.0 NaN NaN
4 NaN NaN 3.0 4.0
5 3.0 NaN NaN 4.0
6 6.0 NaN NaN NaN
Actually, it is producing the desired result but you are trying to calculate diff on nan values which will be nan so diff is working as expected.
For your case just fetch the first column from original dataframe and you should be fine
df2=df1.diff(axis=1)
df2.A=df1.A
print(df2)
Output
A B C D
1 NaN 2.0 4.0