Transposing some columns after groupby in pandas - python

Hey I am struggling with a transformation of a DataFrame:
The initial frame has a format like this:
df=pd.DataFrame({'A':['A1','A1','A1','A1','A1','A2','A2','A2','A2','A3','A3','A3','A3'],
'B':['B1','B1','B1','B1','B2','B2','B2','B3','B3','B3','B4','B4','B4'],
'C':['C1','C1','C1','C2','C2','C3','C3','C4','C4','C5','C5','C6','C6'],
'X':['a','b','c','a','c','a','b','b','c','a','c','a','c'],
'Y':[1,4,4,2,4,1,4,3,1,2,3,4,5]})
A B C X Y
A1 B1 C1 a 1
A1 B1 C1 b 4
A1 B1 C1 c 4
A1 B1 C2 a 2
A1 B2 C2 c 4
A2 B2 C3 a 1
A2 B2 C3 b 4
A2 B3 C4 b 3
A2 B3 C4 c 1
A3 B3 C5 a 2
A3 B4 C5 c 3
A3 B4 C6 a 4
A3 B4 C6 c 5
I have some columns in the beginning where I want to apply groupby and then transpose the last two columns:
First df.groupby(['A','B','C','X']).sum()
Y
A B C X
A1 B1 C1 a 1
b 4
c 4
C2 a 2
B2 C2 c 4
A2 B2 C3 a 1
b 4
B3 C4 b 3
c 1
A3 B3 C5 a 2
B4 C5 c 3
C6 a 4
c 5
and then transpose the X/Y columns and add them horizontally.
A B C a b c
A1 B1 C1 1.0 4.0 4.0
A1 B1 C2 2.0 NaN NaN
A1 B2 C2 NaN NaN 4.0
A2 B2 C3 1.0 4.0 NaN
A2 B3 C4 NaN 3.0 1.0
A3 B3 C5 2.0 NaN NaN
A3 B4 C5 NaN NaN 3.0
A3 B4 C6 4.0 NaN 5.0
Not all groupby rows have all values so they need to be filled with something like np.nan.
The question is linked to this one here but it is more complicated and I couldn't figure it out.

Use Series.unstack for reshape:
df1 = (df.groupby(['A','B','C','X'])['Y'].sum()
.unstack()
.reset_index()
.rename_axis(None, axis=1))
print (df1)
A B C a b c
0 A1 B1 C1 1.0 4.0 4.0
1 A1 B1 C2 2.0 NaN NaN
2 A1 B2 C2 NaN NaN 4.0
3 A2 B2 C3 1.0 4.0 NaN
4 A2 B3 C4 NaN 3.0 1.0
5 A3 B3 C5 2.0 NaN NaN
6 A3 B4 C5 NaN NaN 3.0
7 A3 B4 C6 4.0 NaN 5.0
Alternative with DataFrame.pivot_table:
df1 = (df.pivot_table(index=['A','B','C'],
columns='X',
values='Y',
aggfunc='sum').reset_index().rename_axis(None, axis=1))
print (df1)
A B C a b c
0 A1 B1 C1 1.0 4.0 4.0
1 A1 B1 C2 2.0 NaN NaN
2 A1 B2 C2 NaN NaN 4.0
3 A2 B2 C3 1.0 4.0 NaN
4 A2 B3 C4 NaN 3.0 1.0
5 A3 B3 C5 2.0 NaN NaN
6 A3 B4 C5 NaN NaN 3.0
7 A3 B4 C6 4.0 NaN 5.0

Related

Python Pandas merge two data frames on two keys and get totals

I have two dfs
F1_ID
F2_ID
Event_ID
Date
a1
b2
ab4
5/12/21
a2
b3
ab5
5/12/21
b2
a1
ab4
5/12/21
b3
a2
ab5
5/12/21
the second df has a lot more information on it so I am going to show a filtered version of it.
F1_ID
Event_Name
F2_ID
Event_ID
Date
stats
amount
F1_str_total
F2_str_total
a1
Test
b2
ab1
5/8/21
12
41
13
17
a2
Test1
b3
ab2
5/8/21
16
42
12
54
b2
Test
a1
ab1
5/8/21
-12
-41
0
7
b3
Test1
a2
ab2
5/8/21
-16
-42
87
97
I would like to append the details in df1 to df2 and put None in the missing columns but im not sure how to do this.
Expected Output:
F1_ID
Event_Name
F2_ID
Event_ID
Date
stats
amount
F1_str_total
F2_str_total
a1
Test
b2
ab1
5/8/21
12
41
13
17
a2
Test1
b3
ab2
5/8/21
16
42
12
54
b2
Test
a1
ab1
5/8/21
-12
-41
0
7
b3
Test1
a2
ab2
5/8/21
-16
-42
87
97
a1
None
b2
ab4
5/12/21
None
None
None
None
a2
None
b3
ab5
5/12/21
None
None
None
None
b2
None
a1
ab4
5/12/21
None
None
None
None
b3
None
a2
ab%
5/12/21
None
None
None
None
Simply use pandas.DataFrame.append()
df2 = df2.append(df1, ignore_index=True)
print(df2)
F1_ID Event_Name F2_ID Event_ID Date stats amount F1_str_total \
0 a1 Test b2 ab1 5/8/21 12.0 41.0 13.0
1 a2 Test1 b3 ab2 5/8/21 16.0 42.0 12.0
2 b2 Test a1 ab1 5/8/21 -12.0 -41.0 0.0
3 b3 Test1 a2 ab2 5/8/21 -16.0 -42.0 87.0
4 a1 NaN b2 ab4 5/12/21 NaN NaN NaN
5 a2 NaN b3 ab5 5/12/21 NaN NaN NaN
6 b2 NaN a1 ab4 5/12/21 NaN NaN NaN
7 b3 NaN a2 ab5 5/12/21 NaN NaN NaN
F2_str_total
0 17.0
1 54.0
2 7.0
3 97.0
4 NaN
5 NaN
6 NaN
7 NaN
Or you can use pandas.concat()
df2 = pd.concat([df2, df1], ignore_index=True)

Reshape dataframe by reversing columns names with columns values

I am trying to reshape a df so that the values become columns and the current columns' names become the values of the new df.
Here is an exemple.
df1=pd.DataFrame(data=[['v1','v2','v3'],['v4','v3','v1'],[np.nan,'v2','v1'],['v5','v3','v6'],], columns=['A','B','C'], index=['d1','d2','d3','d4'])
df1.index.names=['Day']
df1 # What I have
Out[1]:
A B C
Day
d1 v1 v2 v3
d2 v4 v3 v1
d3 NaN v2 v1
d4 v5 v3 v6
df2=pd.DataFrame(data=[['A','B','C',np.nan,np.nan,np.nan],['C',np.nan,'B','A',np.nan,np.nan],['C','B',np.nan,np.nan,np.nan,np.nan],[np.nan,np.nan,'B',np.nan,'A','C']], columns=['v1','v2','v3','v4','v5','v6'], index=['d1','d2','d3','d4'])
df2.index.names=['Day']
df2 # Desired output
Out[2]:
v1 v2 v3 v4 v5 v6
d1 A B C NaN NaN NaN
d2 C NaN B A NaN NaN
d3 C B NaN NaN NaN NaN
d4 NaN NaN B NaN A C
I guess something with stack(), unstack() or pivot()?
Try, stack then pivot:
df1a = df1.stack().reset_index()
df1a.pivot('Day', 0, 'level_1')
Output:
0 v1 v2 v3 v4 v5 v6
Day
d1 A B C NaN NaN NaN
d2 C NaN B A NaN NaN
d3 C B NaN NaN NaN NaN
d4 NaN NaN B NaN A C
and reset_index:
df1a.pivot('Day', 0, 'level_1').reset_index()
Output:
0 Day v1 v2 v3 v4 v5 v6
0 d1 A B C NaN NaN NaN
1 d2 C NaN B A NaN NaN
2 d3 C B NaN NaN NaN NaN
3 d4 NaN NaN B NaN A C
You could use a combination of melt and pivot :
(
df1.melt(ignore_index=False)
.dropna()
.pivot(columns="value", values="variable")
.rename_axis(columns=None)
)
v1 v2 v3 v4 v5 v6
Day
d1 A B C NaN NaN NaN
d2 C NaN B A NaN NaN
d3 C B NaN NaN NaN NaN
d4 NaN NaN B NaN A C

Merging Panda DataFrame on Index, with adding additional column, and not having duplicate index

df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3'],
'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index=[0, 1, 2, 3])
df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
'B': ['B4', 'B5', 'B6', 'B7'],
'C': ['C4', 'C5', 'C6', 'C7'],
'D': ['D4', 'D5', 'D6', 'D7']},
index=[4, 5, 6, 7])
df22 = pd.DataFrame({'A2': ['A4', 'A5', 'A6', 'A7'],
'B2': ['B4', 'B5', 'B6', 'B7'],
'C2': ['C4', 'C5', 'C6', 'C7'],
'D2': ['D4', 'D5', 'D6', 'D7']},
index=[4, 5, 6, 7])
frames = [df1, df2, df22]
result = pd.concat(frames,sort=False)
result
As we see, index 4,5,6,7 are repeated, and NAN is added.
How to merge meaningfully .. ?
NaN at A2 ,B2 ,C2, D2, at index 0,1,2,3 is acceptable
But Index 4,5,6,7 should not repeat and should not contain NaN
Do you want something like this? You can pd.concat vertically, the first two dataframes, the join that dataframe to df22 using the dataframe indexes.
pd.concat([df1,df2]).join(df22)
Output:
A B C D A2 B2 C2 D2
0 A0 B0 C0 D0 NaN NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN NaN
2 A2 B2 C2 D2 NaN NaN NaN NaN
3 A3 B3 C3 D3 NaN NaN NaN NaN
4 A4 B4 C4 D4 A4 B4 C4 D4
5 A5 B5 C5 D5 A5 B5 C5 D5
6 A6 B6 C6 D6 A6 B6 C6 D6
7 A7 B7 C7 D7 A7 B7 C7 D7
Another way is to use combine_first:
from functools import reduce
reduce(lambda x,y: x.combine_first(y), [df1,df2,df22])
or
df1.combine_first(df2).combine_first(df22)
Output:
A A2 B B2 C C2 D D2
0 A0 NaN B0 NaN C0 NaN D0 NaN
1 A1 NaN B1 NaN C1 NaN D1 NaN
2 A2 NaN B2 NaN C2 NaN D2 NaN
3 A3 NaN B3 NaN C3 NaN D3 NaN
4 A4 A4 B4 B4 C4 C4 D4 D4
5 A5 A5 B5 B5 C5 C5 D5 D5
6 A6 A6 B6 B6 C6 C6 D6 D6
7 A7 A7 B7 B7 C7 C7 D7 D7
You can first concatenate df1 and df2, and then concatenate df22 with axis=1, like:
>>> pd.concat([pd.concat([df1, df2]), df22], axis=1)
A B C D A2 B2 C2 D2
0 A0 B0 C0 D0 NaN NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN NaN
2 A2 B2 C2 D2 NaN NaN NaN NaN
3 A3 B3 C3 D3 NaN NaN NaN NaN
4 A4 B4 C4 D4 A4 B4 C4 D4
5 A5 B5 C5 D5 A5 B5 C5 D5
6 A6 B6 C6 D6 A6 B6 C6 D6
7 A7 B7 C7 D7 A7 B7 C7 D7
There are several options. For this exact scenario, I would suggest a merge followed by a join. I like this approach, because it feels more like database functions.
df1.merge(df2,how='outer').join(df22,how='outer')
A B C D A2 B2 C2 D2
0 A0 B0 C0 D0 NaN NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN NaN
2 A2 B2 C2 D2 NaN NaN NaN NaN
3 A3 B3 C3 D3 NaN NaN NaN NaN
4 A4 B4 C4 D4 A4 B4 C4 D4
5 A5 B5 C5 D5 A5 B5 C5 D5
6 A6 B6 C6 D6 A6 B6 C6 D6
7 A7 B7 C7 D7 A7 B7 C7 D7

Merge two dataframes in Pandas by taking the mean between the columns

I have the following dataframes:
df1
C1 C2 C3
0 0 0 0
1 0 0 0
df2
C1 C4 C5
0 1 1 1
1 1 1 1
The result I am looking for is:
df3
C1 C2 C3 C4 C5
0 0.5 0 0 1 1
1 0.5 0 0 1 1
Is there an easy way to accomplish this ?
Thanks in advance!
You can using concat and groupby axis =1
s=pd.concat([df1,df2],axis=1)
s.groupby(s.columns.values,axis=1).mean()
Out[116]:
C1 C2 C3 C4 C5
0 0.5 0.0 0.0 1.0 1.0
1 0.5 0.0 0.0 1.0 1.0
A nice alternative from #cᴏʟᴅsᴘᴇᴇᴅ
s.groupby(level=0,axis=1).mean()
Out[117]:
C1 C2 C3 C4 C5
0 0.5 0.0 0.0 1.0 1.0
1 0.5 0.0 0.0 1.0 1.0
DataFrame.add
df3 = df2.add(df1, fill_value=0)
df3[df1.columns.intersection(df2.columns)] /= 2
C1 C2 C3 C4 C5
0 0.5 0.0 0.0 1.0 1.0
1 0.5 0.0 0.0 1.0 1.0
concat + groupby
pd.concat([df1, df2], axis=1).groupby(axis=1, level=0).mean()
C1 C2 C3 C4 C5
0 0.5 0.0 0.0 1.0 1.0
1 0.5 0.0 0.0 1.0 1.0

flatten multiple index into a DataFrame header

Assuming the following DataFrame:
A B C D E F
0 d1 10 d11 10 d21 10
1 d2 30 d12 30 d22 30
2 d3 40 d13 40 d23 40
3 d4 105 d14 105 NaN NaN
4 d5 10 d15 10 NaN NaN
5 d6 30 NaN NaN NaN NaN
6 d7 40 NaN NaN NaN NaN
7 d8 10 NaN NaN NaN NaN
8 d9 5 NaN NaN NaN NaN
9 d10 10 NaN NaN NaN NaN
how do i merge all the descriptions into a single header that is associated with the respective value ?
d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30
0 10 30 40 105 10 30 40 10 5 10 10 30 40 105 10 30 40 10 5 10 10 30 40 105 10 30 40 10 5 10
take note that some descriptions of the original dataframe could have blank values and descriptions (NaN)
i realised i asked something similar before but after putting it into my code it does not achieve what i needed
We can use pd.concat iterating over column pairs i.e
pairs = list(zip(df.columns,df.columns[1:]))[::2]
# [('A', 'B'), ('C', 'D'), ('E', 'F')]
# iterate over pairs and set the first element of pair as index and rename the column name to 0. Then concat and drop na.
ndf = pd.concat([df[list(i)].set_index(i[0]).rename(columns={i[1]:0})
for i in pairs],0).dropna()
d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 \
0 10.0 30.0 40.0 105.0 10.0 30.0 40.0 10.0 5.0 10.0 10.0 30.0
d13 d14 d15 d21 d22 d23
0 40.0 105.0 10.0 10.0 30.0 40.0
r = np.arange(df.shape[1])
a = r % 2
b = r // 2
df.T.set_index([a, b]).T.stack().set_index(0).T
0 d1 d11 d21 d2 d12 d22 d3 d13 d23 d4 d14 d5 d15 d6 d7 d8 d9 d10
1 10 10 10 30 30 30 40 40 40 105 105 10 10 30 40 10 5 10
For fun:-)
pd.DataFrame(sum([df1.values.tolist() for _, df1 in df.groupby((df.dtypes=='object').cumsum(),axis=1)],[])).dropna().set_index(0).T
0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 \
1 10.0 30.0 40.0 105.0 10.0 30.0 40.0 10.0 5.0 10.0 10.0 30.0
0 d13 d14 d15 d21 d22 d23
1 40.0 105.0 10.0 10.0 30.0 40.0

Categories