Stack and Pivot Dataframe in Python - 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

Related

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

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

How to concatenate near duplicate rows in DataFrame

From the original data, there are duplicated data. The duplicates with different DB have to concat to the back of the former one.Is there any way to merge two tables into one as shown below by comparing between data?
From the original data using drop.duplicates and duplicated, i get two tables and wanted to compare them using dictionaries, but by making rows as dictionaries in both the table, the keys are the same in every dictionary which i can't merge them together.
This is the original data given
DB TITLE ISSN IBSN
0 M a 1 NaN
1 M d 1 NaN
2 M c 1 NaN
3 N b 1 NaN
4 N a 1 NaN
5 N d 1 NaN
6 O c 1 NaN
7 O e 1 NaN
8 O a 1 NaN
9 O b 1 NaN
By using drop_duplicates and duplicated:
DB TITLE ISSN IBSN DB TITLE ISSN IBSN
0 M a 1 NaN 0 N a 1 NaN
1 M d 1 NaN 1 N d 1 NaN
2 M c 1 NaN 2 O c 1 NaN
3 N b 1 NaN 3 O a 1 NaN
4 O e 1 NaN 4 O b 1 NaN
This is the kind of dictionary i get from the rows:
{'DB': 'N', 'TITLE': 'a', 'ISSN': 1, 'IBSN': 'NaN'}
{'DB': 'M', 'TITLE': 'a', 'ISSN': 1, 'IBSN': 'NaN'}
I expect the output to be
DB TITLE ISSN IBSN DB TITLE ISSN ISBN DB TITLE ISSN IBSN
0 M a 1.0 NaN N a 1.0 NaN O a 1.0 NaN
1 N b 1.0 NaN O b 1.0 NaN NaN NaN NaN NaN
2 M d 1.0 NaN N d 1.0 NaN NaN NaN NaN NaN
3 M c 1.0 NaN O c 1.0 NaN NaN NaN NaN NaN
4 O e 1.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
The order of 'TITLE' in the column is not important but the DB have to be sorted alphabetically from left to right.
I think the simplest way to do this is using cumcount to segregate sub-groups, then use concat with join='outer':
grps = [
g.set_index('TITLE') for _, g in df.groupby(df.groupby('TITLE').cumcount())
]
pd.concat(grps, join='outer', axis=1, sort=True)
DB ISSN IBSN DB ISSN IBSN DB ISSN IBSN
a M 1 NaN N 1.0 NaN O 1.0 NaN
b N 1 NaN O 1.0 NaN NaN NaN NaN
c M 1 NaN O 1.0 NaN NaN NaN NaN
d M 1 NaN N 1.0 NaN NaN NaN NaN
e O 1 NaN NaN NaN NaN NaN NaN NaN
If you need "TITLE" too, use set_index with drop=False:
grps = [
g.set_index('TITLE', drop=False)
for _, g in df.groupby(df.groupby('TITLE').cumcount())
]
pd.concat(grps, join='outer', axis=1, sort=True)
DB TITLE ISSN IBSN DB TITLE ISSN IBSN DB TITLE ISSN IBSN
a M a 1 NaN N a 1.0 NaN O a 1.0 NaN
b N b 1 NaN O b 1.0 NaN NaN NaN NaN NaN
c M c 1 NaN O c 1.0 NaN NaN NaN NaN NaN
d M d 1 NaN N d 1.0 NaN NaN NaN NaN NaN
e O e 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN

Copying existing columns between DataFrames

having a DataFrame with e.g. 10 columns (a, b, c...) and another smaller one with just let's say 3 of them (d, f, h), what is the 'best' way to copy the columns from the second DataFrame to the first?
The below seems to do the trick but I'm wondering if I should use join, merge or something else instead (for better performance/cleaner code)?
dfOutput = pd.DataFrame(columns=['a','b','c','d','e','f','g','h','i','j'])
melted = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]],columns=['d','h','i'])
dfOutput[melted.columns] = melted[melted.columns]
I believe you need df.merge() and df.reindex():
melted.merge(dfOutput,on=['d','h','i'],how='left').reindex(dfOutput.columns,axis=1)
a b c d e f g h i j
0 NaN NaN NaN 1 NaN NaN NaN 2 3 NaN
1 NaN NaN NaN 4 NaN NaN NaN 5 6 NaN
2 NaN NaN NaN 7 NaN NaN NaN 8 9 NaN
you can reassign this to the first dataframe :
dfOutput = melted.merge(dfOutput,on=['d','h','i'],how='left').reindex(dfOutput.columns,axis=1)
Scenario 2 : If you already have data in certain columns , use dfOutput.update(melted) to update the first dataframe with the second:
For example:
dfOutput:
a b c d e f g h i j
0 NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN 2 NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN 3 NaN NaN NaN NaN NaN NaN
melted:
d h i
0 5 6 7
1 4 8 6
2 7 4 9
>>dfOutput.update(melted)
>>dfOutput
a b c d e f g h i j
0 NaN NaN NaN 5 NaN NaN NaN 6 7 NaN
1 NaN NaN NaN 4 NaN NaN NaN 8 6 NaN
2 NaN NaN NaN 7 NaN NaN NaN 4 9 NaN

Creating pandas column from two columns of strings contains NAs [duplicate]

This question already has answers here:
How to remove nan value while combining two column in Panda Data frame?
(5 answers)
Closed 4 years ago.
I have two columns containing strings and NAs and I want to combine them into 1. I feel this should be fairly simple but cannot seem to get it to work or find the answer on here. Below is the result I am after.
S1 S2 S
A Nan A
A Nan A
A Nan A
A Nan A
Nan C C
Nan C C
Nan C C
Nan C C
Nan Nan Nan
Nan Nan Nan
Nan Nan Nan
B Nan B
B Nan B
B Nan B
B Nan B
B Nan B
I thought df['S'] = df['S1']+ df['S2'] would work but no.
Really feel like there will be an obvious fix, thanks in advance.
Use combine_first:
df['S_new'] = df['S1'].combine_first(df['S2'])
print (df)
S1 S2 S S_new
0 A NaN A A
1 A NaN A A
2 A NaN A A
3 A NaN A A
4 NaN C C C
5 NaN C C C
6 NaN C C C
7 NaN C C C
8 NaN NaN NaN NaN
9 NaN NaN NaN NaN
10 NaN NaN NaN NaN
11 B NaN B B
12 B NaN B B
13 B NaN B B
14 B NaN B B
15 B NaN B B

copying a single-index DataFrame into a MultiIndex DataFrame

Edit: found my answer here: Building a hierarchically indexed DataFrame from existing DataFrames
Turns out I need to create a matching MultiIndex with the higher levels fixed
Original:
I confess, I don't understand the merges and joins yet, but I'm not sure they're what I want.
I have a DataFrame that has a single index, and a DataFrame that has a MultiIndex, the last level of which is the same as the single-index DataFrame.
I am trying to copy/graft the contents in:
In [1]: import pandas as pd
In [2]: import numpy as np
In [3]: import itertools
In [4]:
In [4]: inner = ('a','b')
In [5]: outer = ((10,20), (1,2))
In [6]: cols = ('one','two','three','four')
In [7]:
In [7]: sngl = pd.DataFrame(np.random.randn(2,4), index=inner, columns=cols)
In [8]:
In [8]: index_tups = list(itertools.product(*(outer + (inner,))))
In [9]: index_mult = pd.MultiIndex.from_tuples(index_tups)
In [10]: mult = pd.DataFrame(index=index_mult, columns=cols)
In [11]:
In [11]: sngl
Out[11]:
one two three four
a 2.946876 -0.751171 2.306766 0.323146
b 0.192558 0.928031 1.230475 -0.256739
In [12]: mult
Out[12]:
one two three four
10 1 a NaN NaN NaN NaN
b NaN NaN NaN NaN
2 a NaN NaN NaN NaN
b NaN NaN NaN NaN
20 1 a NaN NaN NaN NaN
b NaN NaN NaN NaN
2 a NaN NaN NaN NaN
b NaN NaN NaN NaN
In [13]:
In [13]: mult.ix[(10,1)] = sngl
In [14]:
In [14]: mult
Out[14]:
one two three four
10 1 a NaN NaN NaN NaN
b NaN NaN NaN NaN
2 a NaN NaN NaN NaN
b NaN NaN NaN NaN
20 1 a NaN NaN NaN NaN
b NaN NaN NaN NaN
2 a NaN NaN NaN NaN
b NaN NaN NaN NaN
In [15]:
What am I doing wrong?
Edit: it works when I do index by index, but that's not the pandas way, surely:
In [15]: mult.ix[(10,1,'a')] = sngl.ix['a']
In [16]: mult
Out[16]:
one two three four
10 1 a 2.946876 -0.7511706 2.306766 0.3231457
b NaN NaN NaN NaN
2 a NaN NaN NaN NaN
b NaN NaN NaN NaN
20 1 a NaN NaN NaN NaN
b NaN NaN NaN NaN
2 a NaN NaN NaN NaN
b NaN NaN NaN NaN
.ix and .loc are equivalent in this example (just more explicit)
In [48]: nm = mult.reset_index().set_index('level_2')
In [49]: nm
Out[49]:
level_0 level_1 one two three four
level_2
a 10 1 NaN NaN NaN NaN
b 10 1 NaN NaN NaN NaN
a 10 2 NaN NaN NaN NaN
b 10 2 NaN NaN NaN NaN
a 20 1 NaN NaN NaN NaN
b 20 1 NaN NaN NaN NaN
a 20 2 NaN NaN NaN NaN
b 20 2 NaN NaN NaN NaN
This should probably work with a series on the rhs; this might be a buglet
In [50]: nm.loc['a',sngl.columns] = sngl.loc['a'].values
In [51]: nm
Out[51]:
level_0 level_1 one two three four
level_2
a 10 1 0.3738456 -0.2261926 -1.205177 0.08448757
b 10 1 NaN NaN NaN NaN
a 10 2 0.3738456 -0.2261926 -1.205177 0.08448757
b 10 2 NaN NaN NaN NaN
a 20 1 0.3738456 -0.2261926 -1.205177 0.08448757
b 20 1 NaN NaN NaN NaN
a 20 2 0.3738456 -0.2261926 -1.205177 0.08448757
b 20 2 NaN NaN NaN NaN
In [52]: nm.reset_index().set_index(['level_0','level_1','level_2'])
Out[52]:
one two three four
level_0 level_1 level_2
10 1 a 0.3738456 -0.2261926 -1.205177 0.08448757
b NaN NaN NaN NaN
2 a 0.3738456 -0.2261926 -1.205177 0.08448757
b NaN NaN NaN NaN
20 1 a 0.3738456 -0.2261926 -1.205177 0.08448757
b NaN NaN NaN NaN
2 a 0.3738456 -0.2261926 -1.205177 0.08448757
b NaN NaN NaN NaN

Categories