Pandas dataframe of dataframes with hierarchical columns - python

I have a hierarchical dataframe I created in pandas:
import pandas as pd
import numpy as np
col_index = pd.MultiIndex.from_product([[0,1], ['a', 'b', 'c']])
df_outer = pd.DataFrame(index=range(4), columns=col_index)
print(df_outer)
0 1
a b c a b c
0 NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN
I'm wondering if it's possible to populate each entry in this data frame with another data frame, for example:
inner_names = ['w', 'x', 'y', 'z']
df_inner = pd.DataFrame(np.random.randn(4,4), index=inner_names, columns=inner_names)
If this is a bad idea, what would be a better way to create some othe easily indexed data structure containing data frames I want to put in the lements of df_outer?

It is a bit crazy, because need 3 levels in columns and 2 levels in indices and then assign by selecting with slicers:
np.random.seed(452)
col_index = pd.MultiIndex.from_product([[0,1], ['a', 'b', 'c'], ['w', 'x']])
idx = pd.MultiIndex.from_product([range(2), ['w', 'x']])
df_outer = pd.DataFrame(columns=col_index, index=idx)
print(df_outer)
0 1
a b c a b c
w x w x w x w x w x w x
0 w NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
x NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 w NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
x NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
inner_names = ['w', 'x']
df_inner = pd.DataFrame(np.random.randn(2,2), index=inner_names, columns=inner_names)
print(df_inner)
w x
w -0.182421 0.962712
x -0.118524 -0.784380
idx = pd.IndexSlice
df_outer.loc[idx[0,:], idx[0, 'a', :]]= df_inner.values
print(df_outer)
0 1
a b c a b c
w x w x w x w x w x w x
0 w -0.182421 0.962712 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
x -0.118524 -0.78438 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 w NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
x NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

Related

How to ask a generated pivot table to include all column combination possibilities?

When use the Python pivot tables, I would like to include all column combination possibilities. For example:
import pandas as pd
from pandas import DataFrame
Result ={
'SenderUserId': ['a', 'a', 'b', 'c', 'c'],
'Date': ['1', '2', '2', '3', '4'],
'RecipientUserId': ['m', 'm', 'n', 'n', 'z'],
'nmail':[1, 2, 3, 3,7]
}
result = DataFrame (Result, columns = ['SenderUserId', 'Date', 'RecipientUserId', 'nmail'])
result = result.pivot_table(index=['SenderUserId'], columns =['Date', 'RecipientUserId'], values = 'nmail').stack()
print (result.head ())
will be producing the following results:
Date 1 2 3 4
SenderUserId RecipientUserId
a m 1.0 2.0 NaN NaN
b n NaN 3.0 NaN NaN
c n NaN NaN 3.0 NaN
z NaN NaN NaN 7.0
However, what I really wanted to get was something like:
Date 1 2 3 4
SenderUserId RecipientUserId
a m 1.0 2.0 NaN NaN
n NaN NaN NaN NaN
z NaN NaN NaN NaN
b m NaN NaN NaN NaN
n NaN 3.0 NaN NaN
z NaN NaN NaN NaN
c m NaN NaN NaN NaN
n NaN NaN 3.0 NaN
z NaN NaN NaN 7.0
As you can see, we just add a few lines where there is no initial column match and assign them NaN. That's okay. As long as this question can be solved, I don't necessarily need to use pivot_table. Any help would be really appreciated!
All the extra rows you're asking for will necessarily be all NaN, so you can just add them in at the end. After doing the pivot, you can use the technique described here to "expand" the resulting index to all possible combinations.
pivot = result.pivot_table(index=['SenderUserId'], columns=['Date', 'RecipientUserId'], values='nmail').stack()
pivot.reindex(pd.MultiIndex.from_product(pivot.index.levels, names=pivot.index.names))
The result:
Date 1 2 3 4
SenderUserId RecipientUserId
a m 1.0 2.0 NaN NaN
n NaN NaN NaN NaN
z NaN NaN NaN NaN
b m NaN NaN NaN NaN
n NaN 3.0 NaN NaN
z NaN NaN NaN NaN
c m NaN NaN NaN NaN
n NaN NaN 3.0 NaN
z NaN NaN NaN 7.0

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 update values in a multi-index dataframe

How can I edit a values of a multi-index dataframe? If it was a non-multi-index dataframe, I know I could do this: df.at[0,'foo'] = 12.3.
Also, this does not work: df.loc[0]['foo']['a'] = 12.3.
Consider a multi-index column dataframe.
colnames = [
['foo', 'foo', 'foo', 'po', 'po', 'po', 'di', 'di', 'di'],
['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
]
df = pd.DataFrame(columns=colnames, index=arange(5))
display(df)
foo po di
a b c a b c a b c
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN
Use tuples for select MultiIndex in columns:
df.loc[0, ('foo','a')] = 12.3
print (df)
foo po di
a b c a b c a b c
0 12.3 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN
If need more complicated updating use slicers:
idx = pd.IndexSlice
df.loc[0, idx['foo', ['b','c']]] = 12.3
print (df)
foo po di
a b c a b c a b c
0 NaN 12.3 12.3 NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN
df.loc[0, idx[:, ['b','c']]] = 12.3
print (df)
foo po di
a b c a b c a b c
0 NaN 12.3 12.3 NaN 12.3 12.3 NaN 12.3 12.3
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN
df.loc[:, idx[['po','di'], 'a']] = 12.3
print (df)
foo po di
a b c a b c a b c
0 NaN NaN NaN 12.3 NaN NaN 12.3 NaN NaN
1 NaN NaN NaN 12.3 NaN NaN 12.3 NaN NaN
2 NaN NaN NaN 12.3 NaN NaN 12.3 NaN NaN
3 NaN NaN NaN 12.3 NaN NaN 12.3 NaN NaN
4 NaN NaN NaN 12.3 NaN NaN 12.3 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.

how to multiply multiple columns by a column in Pandas

I would like to have:
df[['income_1', 'income_2']] * df['mtaz_proportion']
return those columns multiplied by df['mtaz_proportion']
so that I can set
df[['mtaz_income_1', 'mtaz_income_2']] =
df[['income_1', 'income_2']] * df['mtaz_proportion']
but instead I get:
income_1 income_2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ...
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ...
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ...
ect...
what simple thing am I missing?
Thank you!
use multiply method and set axis="index":
df[["A", "B"]].multiply(df["C"], axis="index")
Another way of writing the answer of HYRY:
df.loc[:,['A', 'B']] = df.loc[:,['A', 'B']].multiply(df.loc[:, 'C'], axis="index")
Convert both factors to numpy arrays using to_numpy:
df.loc[:, ['D', 'E']] = df[['A', 'B']].to_numpy() * df[['C']].to_numpy()

Categories