merging multiple columns into one columns in pandas - python-3.x

I have a dataframe called ref(first dataframe) with columns c1, c2 ,c3 and c4.
ref= pd.DataFrame([[1,3,.3,7],[0,4,.5,4.5],[2,5,.6,3]], columns=['c1','c2','c3','c4'])
print(ref)
c1 c2 c3 c4
0 1 3 0.3 7.0
1 0 4 0.5 4.5
2 2 5 0.6 3.0
I wanted to create a new column i.e, c5 ( second dataframe) that has all the values from columns c1,c2,c3 and c4.
I tried concat, merge columns but i cannot get it work.
Please let me know if you have a solutions?

You can use unstack for creating Series from DataFrame and then concat to original:
print (pd.concat([ref, ref.unstack().reset_index(drop=True).rename('c5')], axis=1))
c1 c2 c3 c4 c5
0 1.0 3.0 0.3 7.0 1.0
1 0.0 4.0 0.5 4.5 0.0
2 2.0 5.0 0.6 3.0 2.0
3 NaN NaN NaN NaN 3.0
4 NaN NaN NaN NaN 4.0
5 NaN NaN NaN NaN 5.0
6 NaN NaN NaN NaN 0.3
7 NaN NaN NaN NaN 0.5
8 NaN NaN NaN NaN 0.6
9 NaN NaN NaN NaN 7.0
10 NaN NaN NaN NaN 4.5
11 NaN NaN NaN NaN 3.0
Alternative solution for creating Series is convert df to numpy array by values and then reshape by ravel:
print (pd.concat([ref, pd.Series(ref.values.ravel('F'), name='c5')], axis=1))
c1 c2 c3 c4 c5
0 1.0 3.0 0.3 7.0 1.0
1 0.0 4.0 0.5 4.5 0.0
2 2.0 5.0 0.6 3.0 2.0
3 NaN NaN NaN NaN 3.0
4 NaN NaN NaN NaN 4.0
5 NaN NaN NaN NaN 5.0
6 NaN NaN NaN NaN 0.3
7 NaN NaN NaN NaN 0.5
8 NaN NaN NaN NaN 0.6
9 NaN NaN NaN NaN 7.0
10 NaN NaN NaN NaN 4.5
11 NaN NaN NaN NaN 3.0

using join + ravel('F')
ref.join(pd.Series(ref.values.ravel('F')).to_frame('c5'), how='right')
using join + T.ravel()
ref.join(pd.Series(ref.values.T.ravel()).to_frame('c5'), how='right')
pd.concat + T.stack() + rename
pd.concat([ref, ref.T.stack().reset_index(drop=True).rename('c5')], axis=1)
way too many transposes + append
ref.T.append(ref.T.stack().reset_index(drop=True).rename('c5')).T
combine_first + ravel('F') <--- my favorite
ref.combine_first(pd.Series(ref.values.ravel('F')).to_frame('c5'))
All yield
c1 c2 c3 c4 c5
0 1.0 3.0 0.3 7.0 1.0
1 0.0 4.0 0.5 4.5 0.0
2 2.0 5.0 0.6 3.0 2.0
3 NaN NaN NaN NaN 3.0
4 NaN NaN NaN NaN 4.0
5 NaN NaN NaN NaN 5.0
6 NaN NaN NaN NaN 0.3
7 NaN NaN NaN NaN 0.5
8 NaN NaN NaN NaN 0.6
9 NaN NaN NaN NaN 7.0
10 NaN NaN NaN NaN 4.5
11 NaN NaN NaN NaN 3.0

use the list(zip()) as follows:
d=list(zip(df1.c1,df1.c2,df1.c3,df1.c4))
df2['c5']=pd.Series(d)

try this one, works as you expected
import numpy as np
import pandas as pd
df = pd.DataFrame([[1,2,3,4],[2,3,4,5],[3,4,5,6]], columns=['c1','c2','c3','c4'])
print(df)
r = len(df['c1'])
c = len(list(df))
ndata = list(df.c1) + list(df.c2) + list(df.c3) + list(df.c4)
r = len(ndata) - r
t = r*c
dfnan = pd.DataFrame(np.reshape([np.nan]*t, (r,c)), columns=list(df))
df = df.append(dfnan)
df['c5'] = ndata
print(df)
output is below

This could be a fast option and maybe you can use it inside a loop.
import numpy as np
import pandas as pd
df = pd.DataFrame([[1,2,3,4],[2,3,4,5],[3,4,5,6]], columns=['c1','c2','c3','c4'])
df['c5'] = df.iloc[:,0].astype(str) + df.iloc[:,1].astype(str) + df.iloc[:,2].astype(str) + df.iloc[:,3].astype(str)
Greetings

Related

Fill NaN of selected columns based on a dictionary whose keys are column names and values are content of anther column in Python

For the dataframe df1 as follows:
id products black metal non-ferrous metals precious metal
0 M0066350 copper NaN NaN NaN
1 M0066352 aluminum NaN NaN NaN
2 M0066353 gold NaN NaN NaN
3 M0066354 silver NaN NaN NaN
4 S0200837 soybean NaN NaN NaN
5 S0212350 Apple NaN NaN NaN
6 S0212351 iron ore NaN NaN NaN
7 S0212352 coke NaN NaN NaN
8 S0212353 others 1.0 NaN 1.0
and I hope to fill columns cols = ['black metal', 'non-ferrous metals', 'precious metal'] with 1s based on customized_dict:
customized_dict = {
'black metal': ['iron ore', 'coke'],
'non-ferrous metals': ['copper', 'aluminum'],
'precious metal': ['gold', 'silver']
}
Please note the keys are from column names of df1 and values are from content of products in df1.
So my question is how could I get the following output:
id products black metal non-ferrous metals precious metal
0 M0066350 copper NaN 1.0 NaN
1 M0066352 aluminum NaN 1.0 NaN
2 M0066353 gold NaN NaN 1.0
3 M0066354 silver NaN NaN 1.0
4 S0200837 soybean NaN NaN NaN
5 S0212350 Apple NaN NaN NaN
6 S0212351 iron ore 1.0 NaN NaN
7 S0212352 coke 1.0 NaN NaN
8 S0212353 others 1.0 NaN 1.0
EDIT: new data with duplicates in products column.
id products black metal non-ferrous metals precious metal
0 S0212350 Apple NaN NaN NaN
1 M0066352 aluminum NaN 1.0 NaN
2 S0212352 coke 1.0 NaN NaN
3 S0212354 coke 1.0 NaN NaN
4 M0066350 copper NaN 1.0 NaN
5 M0066353 gold NaN NaN 1.0
6 S0212351 iron ore 1.0 NaN NaN
7 S0212353 others 1.0 NaN 1.0
8 M0066354 silver NaN NaN 1.0
9 S0200837 soybean NaN NaN NaN
Using a simple loop on the columns and update:
customized_dict = {
'black metal': ['iron ore', 'coke'],
'non-ferrous metals': ['copper', 'aluminum'],
'precious metal': ['gold', 'silver']
}
df.update(df.iloc[:,2:].apply(lambda c: c[df['products']
.isin(customized_dict[c.name])]
.fillna(1)))
output:
id products black metal non-ferrous metals precious metal
0 M0066350 copper NaN 1.0 NaN
1 M0066352 aluminum NaN 1.0 NaN
2 M0066353 gold NaN NaN 1.0
3 M0066354 silver NaN NaN 1.0
4 S0200837 soybean NaN NaN NaN
5 S0212350 Apple NaN NaN NaN
6 S0212351 iron ore 1.0 NaN NaN
7 S0212352 coke 1.0 NaN NaN
8 S0212353 others 1.0 NaN 1.0
Use:
# list comprehension for MultiIndex Series with 1
L = [(x, k) for k, v in customized_dict.items() for x in v]
# reshape for DataFrame
df2 = pd.Series(1, index=pd.MultiIndex.from_tuples(L)).unstack()
# replace missing values by products column converted to index
df = df1.set_index('products').combine_first(df2).rename_axis('products').reset_index().reindex(df1.columns, axis=1)
print(df)
id products black metal non-ferrous metals precious metal
0 M0066350 copper NaN 1.0 NaN
1 M0066352 aluminum NaN 1.0 NaN
2 M0066353 gold NaN NaN 1.0
3 M0066354 silver NaN NaN 1.0
4 S0200837 soybean NaN NaN NaN
5 S0212350 Apple NaN NaN NaN
6 S0212351 iron ore 1.0 NaN NaN
7 S0212352 coke 1.0 NaN NaN
8 S0212353 others 1.0 NaN 1.0
Create a reverse dict mapping and use crosstab to create the updated array then fillna:
reversed_dict = {v: k for k, l in customized_dict.items() for v in l}
df1 = df1.fillna(pd.crosstab(df1.index, df1['products'].map(reversed_dict), values=1, aggfunc='mean'))
print(df1)
# Output
id products black metal non-ferrous metals precious metal
0 M0066350 copper NaN 1.0 NaN
1 M0066352 aluminum NaN 1.0 NaN
2 M0066353 gold NaN NaN 1.0
3 M0066354 silver NaN NaN 1.0
4 S0200837 soybean NaN NaN NaN
5 S0212350 Apple NaN NaN NaN
6 S0212351 iron ore 1.0 NaN NaN
7 S0212352 coke 1.0 NaN NaN
8 S0212353 others 1.0 NaN 1.0

Dropna By Column by levels in multiindex and swap for non-na values

I am trying to do some transformations and kind of stuck. Hopefully somebody, can help me out here.
l0 a b c d e f
l1 1 2 1 2 1 2 1 2 1 2 1 2
0 NaN NaN NaN NaN 93.4 NaN NaN NaN NaN NaN 19.0 28.9
1 NaN 9.0 NaN NaN 43.5 32.0 NaN NaN NaN NaN NaN 3.4
2 NaN 5.0 NaN NaN 93.3 83.6 NaN NaN NaN NaN 59.5 28.2
3 NaN 19.6 NaN NaN 72.8 47.4 NaN NaN NaN NaN 31.5 67.2
4 NaN NaN NaN NaN NaN 62.5 NaN NaN NaN NaN NaN 1.8
I have a dataframe, (shown above), and as u can see that, there are multiple 'NaN' with an multiindex column. Selecting the columns along level = 0 (i.e. l0)
I would like to drop the entire column if all are NaN. so, in this case the column's
l0 = ['b', 'd', 'e'] # drop-cols
should be dropped from the Dataframe
l0 a c f
l1 1 2 1 2 1 2
0 NaN NaN 93.4 NaN 19.0 28.9
1 NaN 9.0 43.5 32.0 NaN 3.4
2 NaN 5.0 93.3 83.6 59.5 28.2
3 NaN 19.6 72.8 47.4 31.5 67.2
4 NaN NaN NaN 62.5 NaN 1.8
This will give me the dataframe (as shown above). I would like to then slide values along the rows if all the entries before are null (or swap values between adjacent cols). e.g. Looking at index = 0 i.e. first row.
l0 a c f
l1 1 2 1 2 1 2
0 NaN NaN 93.4 NaN 19.0 28.9
Since, all the values in col - a are null.
I would like to slide / swap values first b/w col - a and col - c.
and then receprocate the same for columns along the right-side i.e. replace entries in col-c with col-f and make all entries in col-f, NaN giving me
l0 a c f
l1 1 2 1 2 1 2
0 93.4 NaN 19.0 28.9 NaN NaN
This is really to save memory for processing and storing information, as interchainging labels ['a', 'b', 'c'...] does not change the meaning of the data.
EDIT: Any Idea's for (2)
I have managed to solve (1) with the following code:
for c in df.columns.get_level_values(0).unique():
if df[c].isna().all().all():
df = df.drop(columns=[c])
df
You can do with all
s=df.isnull().all(level=0,axis=1).all()
df.drop(s.index[s],axis=1,level=0)
Out[55]:
a c f
1 2 1 2 1 2
l1
0 NaN NaN 93.4 NaN 19.0 28.9
1 NaN 9.0 43.5 32.0 NaN 3.4
2 NaN 5.0 93.3 83.6 59.5 28.2
3 NaN 19.6 72.8 47.4 31.5 67.2
4 NaN NaN NaN 62.5 NaN 1.8
groupby and filter
df.groupby(axis=1, level=0).filter(lambda d: ~d.isna().all().all())
a c f
1 2 1 2 1 2
0 NaN NaN 93.4 NaN 19.0 28.9
1 NaN 9.0 43.5 32.0 NaN 3.4
2 NaN 5.0 93.3 83.6 59.5 28.2
3 NaN 19.6 72.8 47.4 31.5 67.2
4 NaN NaN NaN 62.5 NaN 1.8
A little bit shorter
df.groupby(axis=1, level=0).filter(lambda d: ~np.all(d.isna()))

Empty columns when inserted into into df from another df

I tried to add from df5 columns to df_prog. But for some reason they remain empty. I do not understand what I'm doing wrong. Code:
df5['Kol1_1Y']
223520 14.0
223521 65.0
223522 13.0
223523 39.0
223524 13.0
223525 3.0
223526 10.0
223527 19.0
223528 16.0
223529 29.0
Name: Kol1_1Y, dtype: float64
df_prog['Kol1_1Y'] = df5['Kol1_1Y']
df_prog['Kol2_1Y'] = df5['Kol2_1Y']
df_prog['Kol1_3M'] = df5['Kol1_3M']
df_prog['Kol2_3M'] = df5['Kol2_3M']
df_prog.to_excel("C:\python\progGB.xlsx")
df_prog
0 RESPR PREVPR Kol1_1Y Kol2_1Y Kol1_3M Kol2_3M
0 0.4944 0.4944 1.4894 NaN NaN NaN NaN
1 0.7073 0.7073 3.2020 NaN NaN NaN NaN
2 0.3965 0.3965 -0.3989 NaN NaN NaN NaN
3 0.4501 0.4501 -0.1826 NaN NaN NaN NaN
4 0.0271 0.0271 -6.1202 NaN NaN NaN NaN
5 0.2488 0.2488 -2.8447 NaN NaN NaN NaN
6 0.5190 0.5190 0.0176 NaN NaN NaN NaN
7 0.6667 0.6667 2.2334 NaN NaN NaN NaN
8 0.7708 0.7708 4.5216 NaN NaN NaN NaN
9 0.7074 0.7074 2.9906 NaN NaN NaN NaN
Pandas = assignment checking both index and columns. In your case, columns is matched, but index is different. Therefore, it assigns all NaN. To ignore index and columns, you need assigning from numpy ndarray such as:
df_prog['Kol1_1Y'] = df5['Kol1_1Y'].values
df_prog['Kol2_1Y'] = df5['Kol2_1Y'].values
df_prog['Kol1_3M'] = df5['Kol1_3M'].values
df_prog['Kol2_3M'] = df5['Kol2_3M'].values

Replace column values according to corresponding values of other column in Pandas

I am trying to replace value of columns on basis of column. for example col1 has values in first 5 rows and col2 has values so update col1 values according to col2.
For next five rows there are no value in col1 but col2 have value just skip these rows dont need to update col1, and so on.
df9["col1"].replace(["s1"], "data_value", inplace=True)
i used this code line from Replacing few values in a pandas dataframe column with another value. It gives me output just replace value with data value , not replace with values inside data value column.
Dataframe
col1 col2 col3 col4
0 s1 NaN NaN NaN
1 s1 NaN NaN NaN
2 s1 NaN NaN NaN
3 s1 NaN NaN NaN
4 s1 NaN NaN NaN
5 NaN s2 NaN NaN
6 NaN s2 NaN NaN
7 NaN s2 NaN NaN
8 NaN s2 NaN NaN
9 NaN s2 NaN NaN
10 NaN NaN ss1 NaN
11 NaN NaN ss1 NaN
12 NaN NaN ss1 NaN
13 NaN NaN ss1 NaN
14 NaN NaN ss1 NaN
15 NaN NaN NaN ss333
16 NaN NaN NaN ss333
17 NaN NaN NaN ss333
18 NaN NaN NaN ss333
19 NaN NaN NaN ss333
Desired output:
col1 col2 col3 col4
0 0 NaN NaN NaN
1 0 NaN NaN NaN
2 0 NaN NaN NaN
3 0 NaN NaN NaN
4 0 NaN NaN NaN
5 NaN 0 NaN NaN
6 NaN 0 NaN NaN
7 NaN 0 NaN NaN
8 NaN 0 NaN NaN
9 NaN 0 NaN NaN
10 NaN NaN 500 NaN
11 NaN NaN 500 NaN
12 NaN NaN 500 NaN
13 NaN NaN 500 NaN
14 NaN NaN 500 NaN
15 NaN NaN NaN 500
16 NaN NaN NaN 500
17 NaN NaN NaN 500
18 NaN NaN NaN 500
19 NaN NaN NaN 500
Use mask for replace all not missing values with pop for extract column Data:
df = pd.DataFrame({
'A':[4,5] + [np.nan] * 4,
'B':[np.nan,np.nan,9,4,np.nan,np.nan],
'C':[np.nan] * 4 + [7,0],
'Data':list('aaabbb')
})
print (df)
A B C Data
0 4.0 NaN NaN a
1 5.0 NaN NaN a
2 NaN 9.0 NaN a
3 NaN 4.0 NaN b
4 NaN NaN 7.0 b
5 NaN NaN 0.0 b
df = df.mask(df.notnull(), df.pop('Data'), axis=0)
print (df)
A B C
0 a NaN NaN
1 a NaN NaN
2 NaN a NaN
3 NaN b NaN
4 NaN NaN b
5 NaN NaN b
Alternatively you can also use where
df = pd.DataFrame({'col1': ['s1']*5+[np.nan]*15,
'col2':[np.nan]*5+['s2']*5+[np.nan]*10,
'col3':[np.nan]*10+['ss1']*5+[np.nan]*5,
'col4':[np.nan]*15+['ss333']*5,
'data_value':[0]*10+[500]*10 })
df = df.where(df.isnull(), df.pop('data_value'), axis = 0)
col1 col2 col3 col4
0 0 NaN NaN NaN
1 0 NaN NaN NaN
2 0 NaN NaN NaN
3 0 NaN NaN NaN
4 0 NaN NaN NaN
5 NaN 0 NaN NaN
6 NaN 0 NaN NaN
7 NaN 0 NaN NaN
8 NaN 0 NaN NaN
9 NaN 0 NaN NaN
10 NaN NaN 500 NaN
11 NaN NaN 500 NaN
12 NaN NaN 500 NaN
13 NaN NaN 500 NaN
14 NaN NaN 500 NaN
15 NaN NaN NaN 500
16 NaN NaN NaN 500
17 NaN NaN NaN 500
18 NaN NaN NaN 500
19 NaN NaN NaN 500

How to remove rows in a dataframe with more than x number of Null values? [duplicate]

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

Resources