How to create columns by looking not null values in other columns - python

I have the dataframe, that needs to put the not null values into the column.
For example: there maybe more than 5 columns, but no more than 2 not null values each rows
df1 = pd.DataFrame({'A' : [np.nan, np.nan, 'c',np.nan, np.nan, np.nan],
'B' : [np.nan, np.nan, np.nan, 'a', np.nan,'e'],
'C' : [np.nan, 'b', np.nan,'f', np.nan, np.nan],
'D' : [np.nan, np.nan, 'd',np.nan, np.nan, np.nan],
'E' : ['a', np.nan, np.nan,np.nan, np.nan, 'a']})
A B C D E
NaN NaN NaN NaN a
NaN NaN b NaN NaN
c NaN NaN d NaN
NaN a f NaN NaN
NaN NaN NaN NaN NaN
NaN e NaN NaN a
My expected output: To generate 4 new columns, Other_1; Other_1_name; Other_2; Other_2_name, the value will go to Other_1 or Other_2 if there are not null values, and the column name will go to Other_1_name or Other_2_name. if the value is NaN leave the 4 column rows NaN.
A B C D E Other_1 Other_1_name Other_2 Other_2_name
NaN NaN NaN NaN a a E NaN NaN
NaN NaN b NaN NaN b C NaN NaN
c NaN NaN d NaN c A d D
NaN a f NaN NaN a B f C
NaN NaN NaN NaN NaN NaN NaN NaN NaN
NaN e NaN NaN a e B a E

Use DataFrame.melt with missing values by DataFrame.dropna for unpivot, then add counter columns by GroupBy.cumcount and reshape by DataFrame.unstack:
df2 = df1.melt(ignore_index=False,var_name='name',value_name='val').dropna()[['val','name']]
g = df2.groupby(level=0).cumcount().add(1)
df2 = df2.set_index(g,append=True).unstack().sort_index(level=1,axis=1,sort_remaining=False)
df2.columns = df2.columns.map(lambda x: f'Other_{x[1]}_{x[0]}')
print (df2)
Other_1_val Other_1_name Other_2_val Other_2_name
0 a E NaN NaN
1 b C NaN NaN
2 c A d D
3 a B f C
5 e B a E
Last append to original:
df = df1.join(df2)
print (df)
A B C D E Other_1_val Other_1_name Other_2_val Other_2_name
0 NaN NaN NaN NaN a a E NaN NaN
1 NaN NaN b NaN NaN b C NaN NaN
2 c NaN NaN d NaN c A d D
3 NaN a f NaN NaN a B f C
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 NaN e NaN NaN a e B a E

Related

Replacing values with nan based on values of another column

This is my dataframe:
df = pd.DataFrame(
{
'a': [np.nan, np.nan, np.nan, 3333, np.nan, np.nan, 10, np.nan, np.nan, np.nan, np.nan, 200, 100],
'b': [np.nan, 20, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, 100, np.nan, np.nan, np.nan, np.nan]
}
)
And this is the output that I want:
a b
0 NaN NaN
1 NaN 20.0
2 NaN NaN
3 3333.0 NaN
4 NaN NaN
5 NaN NaN
6 NaN NaN
7 NaN NaN
8 NaN 100.0
9 NaN NaN
10 NaN NaN
11 200.0 NaN
12 NaN NaN
Basically if a value in column 'b' is not NaN, I want to keep one value in column a. And then make the rest of values in column a NaN until a value in column b is not NaN.
For example the first case is 20 in column b. After that I want to keep 3333 because this is one value below it which is not NaN and I want to replace 10 with NaN because I've already got one value below b which in this case is 3333 and it is not NaN. The same applies for 100 in column b.
I've searched many posts on stackoverflow and also tried a couple of lines but it didn't work. I guess maybe it can be done by fillna.
One approach
a_notna = df['a'].notna()
m = (a_notna.groupby(df['b'].notna().cumsum())
.cumsum()
.eq(1) & a_notna)
df['a'] = df['a'].where(m)
print(df)
a b
0 NaN NaN
1 NaN 20.0
2 NaN NaN
3 3333.0 NaN
4 NaN NaN
5 NaN NaN
6 NaN NaN
7 NaN NaN
8 NaN 100.0
9 NaN NaN
10 NaN NaN
11 200.0 NaN
12 NaN NaN

Forward fill columns if row value is equal to x - Pandas

I'm trying to forward fill specific columns but only where a row is equal to a certain value. For instance, using the df below, I want to .ffill() Val1, Val2, Helper where rows in Helper = 'Forward'. Everything else should remain the same.
df = pd.DataFrame({
'Col' : ['X',np.nan,np.nan,'Y',np.nan,'Z',np.nan,np.nan,np.nan],
'Val1' : ['B',np.nan,np.nan,'A',np.nan,'C',np.nan,np.nan,np.nan],
'Val2' : ['A',np.nan,np.nan,'C',np.nan,'C',np.nan,np.nan,np.nan],
'Helper' : ['No',np.nan,np.nan,'Forward',np.nan,'Held',np.nan,np.nan,np.nan],
})
mask = df['Helper'].str.contains('Forward', na = True)
df.loc[mask, 'Val1'] = df['Val1']
df['Val1'] = df['Val1'].ffill()
df.loc[mask, 'Val1'] = np.nan
Intended Output:
Col Val1 Val2 Helper
0 X B A No
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 Y A C Forward
4 NaN A C Forward
5 Z C C Held
6 NaN NaN NaN NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
Try this
df.update(df.loc[df['Helper'].str.contains('Forward').ffill(), ['Val1','Val2','Helper']].ffill())
Output
print(df)
Col Val1 Val2 Helper
0 X B A No
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 Y A C Forward
4 NaN A C Forward
5 Z C C Held
6 NaN NaN NaN NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
Create a mask after forward fill and then use the condition to fill the column using np.where
>>> m = df['Helper'].ffill().str.contains('Forward')
>>> req_cols = ['Val1', 'Val2', 'Helper']
>>> df[cols] = np.where(m, df[cols].ffill(), df[cols])
>>> df
Col Val1 Val2 Helper
0 X B A No
1 NaN NaN NaN NaN
2 NaN NaN NaN NaN
3 Y A C Forward
4 NaN A C Forward
5 Z C C Held
6 NaN NaN NaN NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN

how to convert dataframe to multi level dataframe in pandas

I have list and dataframe
list1 = ['one', 'two', 'three']
list2 = ['a', 'b']
df is
A B C D
0 NaN NaN NaN NaN
1 NaN NaN NaN NaN
then how to convert into multilevel indexing dataframe which as follows
A B C D
one two three a b
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN
or
A B
one two three a b C D
0 NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN

pandas groupby: *full* join result of groupwise operation on original index

Consider this df:
import pandas as pd, numpy as np
df = pd.DataFrame.from_dict({'id': ['A', 'B', 'A', 'C', 'D', 'B', 'C'],
'val': [1,2,-3,1,5,6,-2],
'stuff':['12','23232','13','1234','3235','3236','732323']})
Question: how to produce a table with as many columns as unique id ({A, B, C}) and
as many rows as df where, for example for the column corresponding to id==A, the values are:
1,
np.nan,
-2,
np.nan,
np.nan,
np.nan,
np.nan
(that is the result of df.groupby('id')['val'].cumsum() joined on the indexes of df).
UMMM pivot
pd.pivot(df.index,df.id,df.val).cumsum()
Out[33]:
id A B C D
0 1.0 NaN NaN NaN
1 NaN 2.0 NaN NaN
2 -2.0 NaN NaN NaN
3 NaN NaN 1.0 NaN
4 NaN NaN NaN 5.0
5 NaN 8.0 NaN NaN
6 NaN NaN -1.0 NaN
One way via a dictionary comprehension and pd.DataFrame.where:
res = pd.DataFrame({i: df['val'].where(df['id'].eq(i)).cumsum() for i in df['id'].unique()})
print(res)
A B C D
0 1.0 NaN NaN NaN
1 NaN 2.0 NaN NaN
2 -2.0 NaN NaN NaN
3 NaN NaN 1.0 NaN
4 NaN NaN NaN 5.0
5 NaN 8.0 NaN NaN
6 NaN NaN -1.0 NaN
For a small number of groups, you may find this method efficient:
df = pd.concat([df]*1000, ignore_index=True)
def piv_transform(df):
return pd.pivot(df.index, df.id, df.val).cumsum()
def dict_transform(df):
return pd.DataFrame({i: df['val'].where(df['id'].eq(i)).cumsum() for i in df['id'].unique()})
%timeit piv_transform(df) # 17.5 ms
%timeit dict_transform(df) # 8.1 ms
Certainly cleaner answers have been supplied - see pivot.
df1 = pd.DataFrame( data = [df.id == x for x in df.id.unique()]).T.mul(df.groupby(['id']).cumsum().squeeze(),axis=0)
df1.columns =df.id.unique()
df1.applymap(lambda x: np.nan if x == 0 else x)
A B C D
0 1.0 NaN NaN NaN
1 NaN 2.0 NaN NaN
2 -2.0 NaN NaN NaN
3 NaN NaN 1.0 NaN
4 NaN NaN NaN 5.0
5 NaN 8.0 NaN NaN
6 NaN NaN -1.0 NaN
Short and simple:
df.pivot(columns='id', values='val').cumsum()

Stack and Pivot Dataframe in Python

I have a wide dataframe that I want to stack and pivot and can't quite figure out how to do it.
Here is what I am starting with
testdf = pd.DataFrame({"Topic":["A","B","B","C","A"],
"Org":[1,1,2,3,5,],
"DE1":["a","c","d","e","f"],
"DE2":["b","c","a","d","h"],
"DE3":["a","c","b","e","f"] })
testdf
Out[40]:
DE1 DE2 DE3 Org Topic
0 a b a 1 A
1 c c c 1 B
2 d a b 2 B
3 e d e 3 C
4 f h f 5 A
What I would like to do is pivot the table so that the column values for Org are the Column names and the column values for each name are the matching values from D1,D2 and D3 and finally have Topic as the index. Is this even possible?
EDIT: As Randy C pointed out, if I use pivot I can get the following;
testdf.pivot(index = "Topic",columns = "Org")
Out[44]:
DE1 DE2 DE3
Org 1 2 3 5 1 2 3 5 1 2 3 5
Topic
A a NaN NaN f b NaN NaN h a NaN NaN f
B c d NaN NaN c a NaN NaN c b NaN NaN
C NaN NaN e NaN NaN NaN d NaN NaN NaN e NaN
Which is close, but I would like to have it so that the DE values are "stacked" and not wide. The result would look like;
Org 1 2 3 5
Topic
A a NaN NaN f
A b NaN NaN h
A a NaN NaN f
B c d NaN NaN
B c a NaN NaN
B c b NaN NaN
C NaN NaN e NaN
C NaN NaN d NaN
C NaN NaN e NaN
Perhaps:
In[249]: testdf.pivot("Org","Topic").T
Out[249]:
Org 1 2 3 5
Topic
DE1 A a NaN NaN f
B c d NaN NaN
C NaN NaN e NaN
DE2 A b NaN NaN h
B c a NaN NaN
C NaN NaN d NaN
DE3 A a NaN NaN f
B c b NaN NaN
C NaN NaN e NaN
It's not 100% clear to me what your desired output is, but as best I can understand it, .pivot() does seem to be at least close to what you're looking for:
In [8]: testdf.pivot("Topic", "Org")
Out[8]:
DE1 DE2 DE3
Org 1 2 3 5 1 2 3 5 1 2 3 5
Topic
A a NaN NaN f b NaN NaN h a NaN NaN f
B c d NaN NaN c a NaN NaN c b NaN NaN
C NaN NaN e NaN NaN NaN d NaN NaN NaN e NaN

Categories