How to do join of multiindex dataframe with another multiindex dataframe? - python

This is to go further from the following thread:
How to do join of multiindex dataframe with a single index dataframe?
The multi-indices of df1 are sublevel indices of df2.
In [1]: import pandas as pd
In [2]: import numpy as np
In [3]: import itertools
In [4]: inner = ('a','b')
In [5]: outer = ((10,20), (1,2))
In [6]: cols = ('one','two','three','four')
In [7]: sngl = pd.DataFrame(np.random.randn(2,4), index=inner, columns=cols)
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]: 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]: mult.ix[(10,1)] = sngl
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
# the new dataframes
sng2=pd.concat([sng1,sng1],keys=['X','Y'])
mult2=pd.concat([mult,mult],keys=['X','Y'])
In [110]:
sng2
Out[110]:
one two three four
X a 0.206810 -1.056264 -0.572809 -0.314475
b 0.514873 -0.941380 0.132694 -0.682903
Y a 0.206810 -1.056264 -0.572809 -0.314475
b 0.514873 -0.941380 0.132694 -0.682903
In [121]: mult2
Out[121]:
one two three four
X 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
Y 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
the code above is long, please scroll
The two multilevel indices of sng2 share the 1st and 4th indices of mul2. ('X','a') for example.
#DSM proposed a solution to work with a multiindex df2 and single index df1
mult[:] = sngl.loc[mult.index.get_level_values(2)].values
BUt DataFrame.index.get_level_values(2) can only work for one level of index.

It's not clear from the question which index levels the data frames share. I think you need to revise the set-up code as it gives an error at the definition of sngl. Anyway, suppose mult shares the first and second level with sngl you can just drop the second level from the index of mult and index in:
mult[:] = sngl.loc[mult.index.droplevel(2)].values
On a side note, you can construct a multi index from a product directly using pd.MultiIndex.from_product rather than using itertools

Related

Search for starting column and row of column_names in excel file using pandas

I'm analyzing excel files generated by an organization who publishes yearly reports in Excel files. Each year, the column names (Year, A1, B1, C1, etc) remain identical. But each year the organization publishes those column names that start at different row numbers and column numbers.
Each year I manually search for the starting row and column, but it's tedious work given the number of years of reports to wade through.
So I'd like something like this:
...
df = pd.read_excel('test.xlsx')
start_row,start_col = df.find_columns('Year','A1','B1')
...
Thanks.
Let's say you have three .xlsx files on your desktop prefixed with Yearly_Report that when combined in python look like this after reading into one dataframe with something like: df = pd.concat([pd.read_excel(f, header=None) for f in yearly_files]):
0 1 2 3 4 5 6 7 8 9 10
0 A B C NaN NaN NaN NaN NaN NaN NaN NaN
1 1 2 3 NaN NaN NaN NaN NaN NaN NaN NaN
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN A B C NaN NaN NaN NaN NaN NaN
4 NaN NaN 4 5 6 NaN NaN NaN NaN NaN NaN
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN A B C
2 NaN NaN NaN NaN NaN NaN NaN NaN 4 5 6
As you can see, the columns and values are scattered across various columns and rows. The following steps would get you the desired result. First, you need to pd.concat the files and .dropna rows. Then, transpose the dataframe with .T before removing all cells with NaN values. Next, revert the dataframe back with another transpose .T. Finally, simply name the columns and drop rows that are equal to the column headers.
import glob, os
import pandas as pd
main_folder = 'Desktop/'
yearly_files = glob.glob(f'{main_folder}Yearly_Report*.xlsx')
df = pd.concat([pd.read_excel(f, header=None) for f in yearly_files]) \
.dropna(how='all').T \
.apply(lambda x: pd.Series(x.dropna().values)).T
df.columns = ['A','B','C']
df = df[df['A'] != 'A']
df
output:
A B C
1 1 2 3
4 4 5 6
2 4 5 6
Soething Like this not totally sure what you are looking for
df = pd.read_excel('test.xlsx')
for i in df.index:
print(df.loc[i,'Year'])
print(df.loc[i, 'A1'])
print(df.loc[i, "B1"])

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- How to append a transpose of dataframe to another dataframe as column headers?

I need to add DF1 values as column in DF2
DataFrame1
DataFrame2 (in which append operation is required)
Required DataFrame - DF3
Try pd.concat with an empty dataframe consisting only of headers. Here's a demo -
df1
A B
0 -0.820067 -0.078793
1 -0.341793 -0.301040
2 -0.122264 1.163896
3 -1.693027 0.147647
4 -1.322206 1.839631
5 0.902077 0.334976
6 0.628941 -1.252080
7 0.607116 -0.588056
8 0.564448 0.096036
9 -0.863496 0.345668
df2
HeaderName
0 XYZ
1 ABC
2 SRT
3 FFF
pd.concat([df1, pd.DataFrame(columns=df2.HeaderName)], 1)
A B XYZ ABC SRT FFF
0 -0.820067 -0.078793 NaN NaN NaN NaN
1 -0.341793 -0.301040 NaN NaN NaN NaN
2 -0.122264 1.163896 NaN NaN NaN NaN
3 -1.693027 0.147647 NaN NaN NaN NaN
4 -1.322206 1.839631 NaN NaN NaN NaN
5 0.902077 0.334976 NaN NaN NaN NaN
6 0.628941 -1.252080 NaN NaN NaN NaN
7 0.607116 -0.588056 NaN NaN NaN NaN
8 0.564448 0.096036 NaN NaN NaN NaN
9 -0.863496 0.345668 NaN NaN NaN NaN
Use DataFrame.join:
df2 = df1.join(pd.DataFrame(columns=df2['HeaderName']))
Or assign:
df2 = df1.assign(**pd.Series(index=df2['HeaderName']))
We can using reindex
df1.reindex(columns=list(df1)+df2.HeaderName.tolist())
Out[754]:
A B XYZ ABC SRT FFF
0 -0.820067 -0.078793 NaN NaN NaN NaN
1 -0.341793 -0.301040 NaN NaN NaN NaN
2 -0.122264 1.163896 NaN NaN NaN NaN
3 -1.693027 0.147647 NaN NaN NaN NaN
4 -1.322206 1.839631 NaN NaN NaN NaN
5 0.902077 0.334976 NaN NaN NaN NaN
6 0.628941 -1.252080 NaN NaN NaN NaN
7 0.607116 -0.588056 NaN NaN NaN NaN
8 0.564448 0.096036 NaN NaN NaN NaN
9 -0.863496 0.345668 NaN NaN NaN NaN

Move/shift values in Pandas Data Frame

Assuming an example of a data frame df:
A
0 4.3
1 75
2 8.5
3 4.0
4 98
I would need to move each value from column A to each column - one value per column:
starting from second value: move to second column B,
third value to third column C,
and so on...
Desired output:
A B C D E
0 4.3 NaN NaN NaN NaN
1 NaN 75 NaN NaN NaN
2 NaN NaN 8.5 NaN NaN
3 NaN NaN NaN 4.0 NaN
4 NaN NaN NaN Nan 98
One idea was to copy each value to second column and then erase it in previous column or to shift value from one column to another but I'm not sure how to apply this...
MWE
import pandas as pd
import numpy as np
df=pd.DataFrame(data=np.random.randint(0,100,(5,5)), columns=['A','B','C','D','E'])
df.iloc[:,1:] =np.nan
df.iloc[[1],[1]] = df.iloc[[1],[0]]
df.iloc[[1],[1]] = df.iloc[[1],[0]].shift(1,axis=1)
In [76]: import string
In [77]: r = pd.DataFrame(np.eye(len(df)),
columns=list(string.ascii_uppercase[:len(df)])) \
.replace(0, np.nan) * df.A.values
In [78]: r
Out[78]:
A B C D E
0 4.3 NaN NaN NaN NaN
1 NaN 75.0 NaN NaN NaN
2 NaN NaN 8.5 NaN NaN
3 NaN NaN NaN 4.0 NaN
4 NaN NaN NaN NaN 98.0
or better:
In [11]: r = pd.DataFrame(index=df.index, columns=list(string.ascii_uppercase[:len(df)]))
In [12]: np.fill_diagonal(r.values, df.A)
In [13]: r
Out[13]:
A B C D E
0 4.3 NaN NaN NaN NaN
1 NaN 75 NaN NaN NaN
2 NaN NaN 8.5 NaN NaN
3 NaN NaN NaN 4 NaN
4 NaN NaN NaN NaN 98
UPDATE:
how to "move" single value
we can use Series.shift method.
move horizontally:
In [94]: r.loc[1] = r.loc[1].shift(3)
In [95]: r
Out[95]:
A B C D E
0 4.3 NaN NaN NaN NaN
1 NaN NaN NaN NaN 75.0
2 NaN NaN 8.5 NaN NaN
3 NaN NaN NaN 4.0 NaN
4 NaN NaN NaN NaN 98.0
move vertically:
In [96]: r.loc[:, 'D'] = r.loc[:, 'D'].shift(-2)
In [97]: r
Out[97]:
A B C D E
0 4.3 NaN NaN NaN NaN
1 NaN NaN NaN 4.0 75.0
2 NaN NaN 8.5 NaN NaN
3 NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN 98.0
NOTE: shift will shift the whole row/column, but as soon as we have only one value in each row/column this will work.
>>> import pandas as pd
>>> import numpy as np
>>> df = pd.DataFrame({'A':[4.3, 75, 8.5, 4.0, 98]})
>>> df
A
0 4.3
1 75.0
2 8.5
3 4.0
4 98.0
>>> diag_df = pd.DataFrame(np.diag(df.A), index=df.index, columns=['A', 'B', 'C', 'D', 'E'])
>>> diag_df.replace(0, np.nan, inplace=True)
>>> diag_df
A B C D E
0 4.3 NaN NaN NaN NaN
1 NaN 75.0 NaN NaN NaN
2 NaN NaN 8.5 NaN NaN
3 NaN NaN NaN 4.0 NaN
4 NaN NaN NaN NaN 98.0
Keep in mind that if you have 0 along the diagonal then it will be replaced with NaN if you use the replace method this way.

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