Calculating multiple columns using Panda's mask() and diff() with multiple conditions - python
I have a dataframe df
df:
Date
Type
AVG1
AVG2
AVG3
AVG4
AVG5
2022-05
ROL1
0.33
0.45
0.12
0.96
1.33
2022-05
ROL2
1.43
0.11
0.75
1.99
3.01
2022-05
ROL3
0.11
0.32
0.55
1.26
4.22
2022-04
ROL1
1.66
0.71
0.87
5.88
1.11
2022-04
ROL2
2.31
0.89
2.20
4.36
4.87
2022-04
ROL3
5.40
1.22
4.45
0.01
0.31
And I need to create the columns AVG1_ROL1_MoM, AVG1_ROL2_MoM, AVG3_ROL1_MoM, AVG1_ROL2_MoM and so on. Where AVG1_ROL1_MoM is the difference in AVG1 where TYPE = ROL1 from one month to the other:
Date
Type
AVG1
AVG2
AVG3
AVG4
AVG5
AVG1_ROL1_MoM
AVG1_ROL2_MoM
2022-05
ROL1
0.33
0.45
0.12
0.96
1.33
-1.33
NaN
2022-05
ROL2
1.43
0.11
0.75
1.99
3.01
NaN
-0.88
2022-05
ROL3
0.11
0.32
0.55
1.26
4.22
NaN
NaN
2022-04
ROL1
1.66
0.71
0.87
5.88
1.11
NaN
NaN
2022-04
ROL2
2.31
0.89
2.20
4.36
4.87
NaN
NaN
2022-04
ROL3
5.40
1.22
4.45
0.01
0.31
NaN
NaN
I tried to do that with mask() and shift(), but it didn't work:
df['AVG1_ROL1_MoM'] = df.mask(df['Type']=="ROL1", df['AVG1'] - df['AVG1'].shift(), inplace=True)
This returns that an axis must be defined, but when I define and axis it returns that:
"Cannot do inplace boolean setting on mixed-types with a non np.nan value"
What would be the best approach for this?
melt the dataframe to get all the values in a single column
Create the new column names
groupby to find the monthly differences
pivot to get back the original structure
merge with the original dataframe
melted = df.melt(["Date","Type"])
melted["column"] = melted["variable"]+"_"+melted["Type"]+"_MoM"
melted["diff"] = melted.groupby(["Type","variable"])["value"].diff(-1)
pivoted = melted.pivot(["Date","Type"],"column","diff").sort_index(ascending=[False,True]).reset_index()
output = df.merge(pivoted, on=["Date","Type"])
>>> output
Date Type AVG1 ... AVG5_ROL1_MoM AVG5_ROL2_MoM AVG5_ROL3_MoM
0 2022-05 ROL1 0.33 ... 0.22 NaN NaN
1 2022-05 ROL2 1.43 ... NaN -1.86 NaN
2 2022-05 ROL3 0.11 ... NaN NaN 3.91
3 2022-04 ROL1 1.66 ... NaN NaN NaN
4 2022-04 ROL2 2.31 ... NaN NaN NaN
5 2022-04 ROL3 5.40 ... NaN NaN NaN
[6 rows x 22 columns]
IUUC, you can try group by Type column and then compare the subgroup AVG shifted value and rename the outcome columns:
out = (df.filter(like='AVG')
.groupby(df['Type'])
.apply(lambda g: (g-g.shift(-1)).rename(columns=lambda col: f'{col}_{g.name}_MOM'))
)
print(out)
AVG1_ROL1_MOM AVG2_ROL1_MOM AVG3_ROL1_MOM AVG4_ROL1_MOM AVG5_ROL1_MOM \
0 -1.33 -0.26 -0.75 -4.92 0.22
1 NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN
AVG1_ROL2_MOM AVG2_ROL2_MOM AVG3_ROL2_MOM AVG4_ROL2_MOM AVG5_ROL2_MOM \
0 NaN NaN NaN NaN NaN
1 -0.88 -0.78 -1.45 -2.37 -1.86
2 NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN
AVG1_ROL3_MOM AVG2_ROL3_MOM AVG3_ROL3_MOM AVG4_ROL3_MOM AVG5_ROL3_MOM
0 NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN
2 -5.29 -0.9 -3.9 1.25 3.91
3 NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN
out = pd.concat([df, out], axis=1)
print(out)
Date Type AVG1 AVG2 AVG3 AVG4 AVG5 AVG1_ROL1_MOM AVG2_ROL1_MOM \
0 2022-05 ROL1 0.33 0.45 0.12 0.96 1.33 -1.33 -0.26
1 2022-05 ROL2 1.43 0.11 0.75 1.99 3.01 NaN NaN
2 2022-05 ROL3 0.11 0.32 0.55 1.26 4.22 NaN NaN
3 2022-04 ROL1 1.66 0.71 0.87 5.88 1.11 NaN NaN
4 2022-04 ROL2 2.31 0.89 2.20 4.36 4.87 NaN NaN
5 2022-04 ROL3 5.40 1.22 4.45 0.01 0.31 NaN NaN
AVG3_ROL1_MOM AVG4_ROL1_MOM AVG5_ROL1_MOM AVG1_ROL2_MOM AVG2_ROL2_MOM \
0 -0.75 -4.92 0.22 NaN NaN
1 NaN NaN NaN -0.88 -0.78
2 NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN
AVG3_ROL2_MOM AVG4_ROL2_MOM AVG5_ROL2_MOM AVG1_ROL3_MOM AVG2_ROL3_MOM \
0 NaN NaN NaN NaN NaN
1 -1.45 -2.37 -1.86 NaN NaN
2 NaN NaN NaN -5.29 -0.9
3 NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN
AVG3_ROL3_MOM AVG4_ROL3_MOM AVG5_ROL3_MOM
0 NaN NaN NaN
1 NaN NaN NaN
2 -3.9 1.25 3.91
3 NaN NaN NaN
4 NaN NaN NaN
5 NaN NaN NaN
Related
Moving forward in a panda dataframe looking for the first occurrence of multi-conditions with reset
I am having trouble with multi-conditions moving forward in a dataframe. Here's a simplification of my model: import pandas as pd import numpy as np df = pd.DataFrame({ 'date':pd.date_range(start='2022-05-12', periods=27), 'l': [10.0,9.9,11.1,10.9,12.1,9.6,13.1,17.9,18.0,15.6,13.5,14.2,10.5,9.5,7.6,9.8,10.2,15.3,17.7,21.8,10.9,18.9,16.4,13.3,7.1,6.8,9.4], 'c': [10.5,10.2,12.0,11.7,13.5,10.9,13.9,18.2,18.8,16.2,15.1,14.8,11.8,10.1,8.9,10.5,11.1,16.9,19.8,22.0,15.5,20.1,17.7,14.8,8.9,7.3,10.1], 'h': [10.8,11.5,13.4,13.6,14.2,11.4,15.8,18.5,19.2,16.9,16.0,15.3,12.9,10.5,9.2,11.1,12.3,18.5,20.1,23.5,21.1,20.5,18.2,15.4,9.6,8.4,10.5], 'oc': [False,True,False,False,False,True,True,True,False,False,True,False,True,False,False,False,False,True,False,False,False,False,False,False,False,False,False], 's': [np.nan,9.3,np.nan,np.nan,np.nan,14.5,14.4,np.nan,np.nan,np.nan,8.1,np.nan,10.7,np.nan,np.nan,np.nan,np.nan,6.9,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan], 'i': [np.nan,9.0,np.nan,np.nan,np.nan,13.6,13.4,np.nan,np.nan,np.nan,7.0,np.nan,9.9,np.nan,np.nan,np.nan,np.nan,9.2,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan], 't': [np.nan,15.5,np.nan,np.nan,np.nan,16.1,15.9,np.nan,np.nan,np.nan,16.5,np.nan,17.2,np.nan,np.nan,np.nan,np.nan,25.0,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan,np.nan] }) df = df.set_index('date') # df Index is datetime type print(df) l c h oc s i t date 2022-05-12 10.0 10.5 10.8 False NaN NaN NaN 2022-05-13 9.9 10.2 11.5 True 9.3 9.0 15.5 2022-05-14 11.1 12.0 13.4 False NaN NaN NaN 2022-05-15 10.9 11.7 13.6 False NaN NaN NaN 2022-05-16 12.1 13.5 14.2 False NaN NaN NaN 2022-05-17 9.6 10.9 11.4 True 14.5 13.6 16.1 2022-05-18 13.1 13.9 15.8 True 14.4 13.4 15.9 2022-05-19 17.9 18.2 18.5 True NaN NaN NaN 2022-05-20 18.0 18.8 19.2 False NaN NaN NaN 2022-05-21 15.6 16.2 16.9 False NaN NaN NaN 2022-05-22 13.5 15.1 16.0 True 8.1 7.0 16.5 2022-05-23 14.2 14.8 15.3 False NaN NaN NaN 2022-05-24 10.5 11.8 12.9 True 10.7 9.9 17.2 2022-05-25 9.5 10.1 10.5 False NaN NaN NaN 2022-05-26 7.6 8.9 9.2 False NaN NaN NaN 2022-05-27 9.8 10.5 11.1 False NaN NaN NaN 2022-05-28 10.2 11.1 12.3 False NaN NaN NaN 2022-05-29 15.3 16.9 18.5 True 6.9 9.2 25.0 2022-05-30 17.7 19.8 20.1 False NaN NaN NaN 2022-05-31 21.8 22.0 23.5 False NaN NaN NaN 2022-06-01 10.9 15.5 21.1 False NaN NaN NaN 2022-06-02 18.9 20.1 20.5 False NaN NaN NaN 2022-06-03 16.4 17.7 18.2 False NaN NaN NaN 2022-06-04 13.3 14.8 15.4 False NaN NaN NaN 2022-06-05 7.1 8.9 9.6 False NaN NaN NaN 2022-06-06 6.8 7.3 8.4 False NaN NaN NaN 2022-06-07 9.4 10.1 10.5 False NaN NaN NaN This is the result I am trying to achieve: date l c h oc s i t cc diff r 0 2022-05-12 10.0 10.5 10.8 False NaN NaN NaN NaN NaN NaN 1 2022-05-13 9.9 10.2 11.5 True 9.3 9.0 15.5 NaN NaN NaN 2 2022-05-14 11.1 12.0 13.4 False NaN NaN NaN NaN NaN NaN 3 2022-05-15 10.9 11.7 13.6 False NaN NaN NaN NaN NaN NaN 4 2022-05-16 12.1 13.5 14.2 False NaN NaN NaN NaN NaN NaN 5 2022-05-17 9.6 10.9 11.4 True 14.5 13.6 16.1 NaN NaN NaN 6 2022-05-18 13.1 13.9 15.8 True 14.4 13.4 15.9 True 5.3 t 7 2022-05-19 17.9 18.2 18.5 True NaN NaN NaN NaN NaN NaN 8 2022-05-20 18.0 18.8 19.2 False NaN NaN NaN NaN NaN NaN 9 2022-05-21 15.6 16.2 16.9 False NaN NaN NaN NaN NaN NaN 10 2022-05-22 13.5 15.1 16.0 True 8.1 7.0 16.5 NaN NaN NaN 11 2022-05-23 14.2 14.8 15.3 False NaN NaN NaN NaN NaN NaN 12 2022-05-24 10.5 11.8 12.9 True 10.7 9.9 17.2 NaN NaN NaN 13 2022-05-25 9.5 10.1 10.5 False NaN NaN NaN NaN NaN NaN 14 2022-05-26 7.6 8.9 9.2 False NaN NaN NaN True -7.0 s 15 2022-05-27 9.8 10.5 11.1 False NaN NaN NaN NaN NaN NaN 16 2022-05-28 10.2 11.1 12.3 False NaN NaN NaN NaN NaN NaN 17 2022-05-29 15.3 16.9 18.5 True 6.9 9.2 25.0 NaN NaN NaN 18 2022-05-30 17.7 19.8 20.1 False NaN NaN NaN NaN NaN NaN 19 2022-05-31 21.8 22.0 23.5 False NaN NaN NaN NaN NaN NaN 20 2022-06-01 10.9 15.5 21.1 False NaN NaN NaN NaN NaN NaN 21 2022-06-02 18.9 20.1 20.5 False NaN NaN NaN NaN NaN NaN 22 2022-06-03 16.4 17.7 18.2 False NaN NaN NaN NaN NaN NaN 23 2022-06-04 13.3 14.8 15.4 False NaN NaN NaN NaN NaN NaN 24 2022-06-05 7.1 8.9 9.6 False NaN NaN NaN True -7.7 i 25 2022-06-06 6.8 7.3 8.4 False NaN NaN NaN NaN NaN NaN 26 2022-06-07 9.4 10.1 10.5 False NaN NaN NaN NaN NaN NaN Principles: We always move forward in the dataframe When oc is True we 'memorize' both c, s, i and t values from this row Moving forward we look for the first occurrence of one of the following conditions: h >= t l <= s l <= i When it happens we set cc to True and we calculate the difference of the 'memorized' values when oc was True and write a letter to distinguish the condition: If h >= t: diff = t-c and r = 't' If l <= s: diff = s-c and r = 's' If l <= i: diff = i-c and r = 'i' Once one of the conditions has been met, we look again for oc is True and then the conditions to be met, until the end of the dataframe. If oc is True again before one of the conditions has been met, we omit it. What happens chronologically: 2022-05-13: oc is True so we memorize c, s, i, t 2022-05-17: oc is True but none of the conditions have been met, yet -> omission 2022-05-18: h > t[2022-05-13] -> diff = t[2022-05-13]-c[2022-05-13] = 15.5-10.2 = 5.3, r = 't' 2022-05-22: oc is True so we memorize c, s, i, t 2022-05-24: oc is True but none of the conditions have been met, yet -> omission 2022-05-26: l < s[2022-05-22] -> diff = s[2022-05-22]-c[2022-05-22] = 8.1-15.1 = -7.0, r = 's' 2022-05-29: oc is True so we memorize c, s, i, t 2022-06-05: l < i[2022-05-29] -> diff = i[2022-05-29]-c[2022-05-29] = 9.2-16.9 = -7.7, r = 'i' A loop works but take an enormous amount of time, if possible I'd like to avoid it. I've tried a really good solution from Baron Legendre described here which works perfectly when looking for equal values but I can't seem to adapt it to my model. Also I'm having an index problem: I'm getting different results when using a datetime Index even when I reset it. I've been stuck with that problem for a while now so any help would gladly be appreciated.
IIUC, you can use the commented code below: mem = False # Memory flag data = [] # Store new values # Create groups to speed the process (remove rows before first valid oc) grp = df['oc'].cumsum().loc[lambda x: x > 0] # For each group for _, subdf in df.groupby(grp): # Memorize new oc fields (c, s, i, t) if not mem: oc = subdf.iloc[0][['c', 's', 'i', 't']] mem = True # Extract l and h fields lh = subdf.iloc[1:][['l', 'h']] # Try to extract the first row where one of conditions is met sr = (pd.concat([lh['h'] >= oc['t'], lh['l'] <= oc['s'], lh['l'] <= oc['i']], keys=['t', 's', 'i'], axis=1) .rename_axis(columns='r').stack().rename('cc') .loc[lambda x: x].head(1).reset_index('r').squeeze()) # Keep this row if exists and unlock memory if not sr.empty: sr['diff'] = oc[sr['r']] - oc['c'] data.append(sr) mem = False # Merge new values out = df.join(pd.concat(data, axis=1).T[['cc', 'r', 'diff']]) Output: >>> out l c h oc s i t cc r diff date 2022-05-12 10.0 10.5 10.8 False NaN NaN NaN NaN NaN NaN 2022-05-13 9.9 10.2 11.5 True 9.3 9.0 15.5 NaN NaN NaN 2022-05-14 11.1 12.0 13.4 False NaN NaN NaN NaN NaN NaN 2022-05-15 10.9 11.7 13.6 False NaN NaN NaN NaN NaN NaN 2022-05-16 12.1 13.5 14.2 False NaN NaN NaN NaN NaN NaN 2022-05-17 9.6 10.9 11.4 True 14.5 13.6 16.1 NaN NaN NaN 2022-05-18 13.1 13.9 15.8 False NaN NaN NaN True t 5.3 2022-05-19 17.9 18.2 18.5 False NaN NaN NaN NaN NaN NaN 2022-05-20 18.0 18.8 19.2 False NaN NaN NaN NaN NaN NaN 2022-05-21 15.6 16.2 16.9 False NaN NaN NaN NaN NaN NaN 2022-05-22 13.5 15.1 16.0 True 8.1 7.0 16.5 NaN NaN NaN 2022-05-23 14.2 14.8 15.3 False NaN NaN NaN NaN NaN NaN 2022-05-24 10.5 11.8 12.9 True 10.7 9.9 17.2 NaN NaN NaN 2022-05-25 9.5 10.1 10.5 False NaN NaN NaN NaN NaN NaN 2022-05-26 7.6 8.9 9.2 False NaN NaN NaN True s -7.0 2022-05-27 9.8 10.5 11.1 False NaN NaN NaN NaN NaN NaN 2022-05-28 10.2 11.1 12.3 False NaN NaN NaN NaN NaN NaN 2022-05-29 15.3 16.9 18.5 True 6.9 9.2 25.0 NaN NaN NaN 2022-05-30 17.7 19.8 20.1 False NaN NaN NaN NaN NaN NaN 2022-05-31 21.8 22.0 23.5 False NaN NaN NaN NaN NaN NaN 2022-06-01 10.9 15.5 21.1 False NaN NaN NaN NaN NaN NaN 2022-06-02 18.9 20.1 20.5 False NaN NaN NaN NaN NaN NaN 2022-06-03 16.4 17.7 18.2 False NaN NaN NaN NaN NaN NaN 2022-06-04 13.3 14.8 15.4 False NaN NaN NaN NaN NaN NaN 2022-06-05 7.1 8.9 9.6 False NaN NaN NaN True i -7.7 2022-06-06 6.8 7.3 8.4 False NaN NaN NaN NaN NaN NaN 2022-06-07 9.4 10.1 10.5 False NaN NaN NaN NaN NaN NaN
How to convert multiple set of column to single column in pandas?
i want to convert a columns(Azi_0 to Azi_47,Dist_0 to Dist_47) in dataframe(df) to a two column(Azimuth,Distance) as in new_df? Azi = [f"Azi_{i}" for i in range(47)] dist = [f"Dist_{i}" for i in range(47)] sample dataframe,df: expected output,new_df: Current_Sim_Az_obj1 Current_Sim_distance_r_obj1 Azimuth Distance -60 3.950372041 -59.73007665 3.07 -60 3.950372041 -59.73007665 3.07 -60 6.950372041 -59.4701257 7.89 -60 6.950372041 -59.89004647 7.765 -60 8.950372041 -59.64009363 8.345 -60 8.950372041 -59.58010495 8.425 -60 8.950372041 -59.58010495 8.425 -55 2.38397709 -55.06095763 3.14 -55 2.38397709 -55.21092934 3.065 -55 2.38397709 -55.21092934 3.065 -55 2.38397709 -55.2609199 3.03 -55 2.38397709 -55.2609199 3.03 -55 2.38397709 -55.2609199 3.03 -55 2.38397709 -55.2609199 3.03 -55 2.38397709 -55.03096329 3.105 -55 2.38397709 -55.03096329 3.105 -55 2.38397709 -55.32090858 3 -55 2.38397709 -55.32090858 3 -55 2.38397709 -55.27091802 3.12 -55 2.38397709 -55.27091802 3.12 -55 2.38397709 -55.8508086 3.09 -55 2.38397709 -55.8508086 3.09 -55 2.38397709 -55.57086142 3.065 -55 2.38397709 -55.57086142 3.065 How to combine several columns to a single column?
You are essentially asking how to coalesce a values of certain df-columns into one column - you can do it like this: from random import choice import pandas as pd # all azimuth names azi_names = [f"Azi_{i}" for i in range(5)] # all distance names dist_names = [f"Dist_{i}" for i in range(5)] df = pd.DataFrame(columns = azi_names + dist_names) # put some values in for i in range(20): k = choice(range(5)) df = df.append({f"Azi_{k}": i, f"Dist_{k}": i}, ignore_index=True) print(df) which randomly creates: Azi_0 Azi_1 Azi_2 Azi_3 Azi_4 Dist_0 Dist_1 Dist_2 Dist_3 Dist_4 0 NaN NaN NaN 0.0 NaN NaN NaN NaN 0.0 NaN 1 NaN 1.0 NaN NaN NaN NaN 1.0 NaN NaN NaN 2 2.0 NaN NaN NaN NaN 2.0 NaN NaN NaN NaN 3 NaN NaN 3.0 NaN NaN NaN NaN 3.0 NaN NaN 4 NaN 4.0 NaN NaN NaN NaN 4.0 NaN NaN NaN 5 NaN NaN NaN NaN 5.0 NaN NaN NaN NaN 5.0 6 6.0 NaN NaN NaN NaN 6.0 NaN NaN NaN NaN 7 NaN 7.0 NaN NaN NaN NaN 7.0 NaN NaN NaN 8 NaN 8.0 NaN NaN NaN NaN 8.0 NaN NaN NaN 9 9.0 NaN NaN NaN NaN 9.0 NaN NaN NaN NaN 10 NaN NaN 10.0 NaN NaN NaN NaN 10.0 NaN NaN 11 11.0 NaN NaN NaN NaN 11.0 NaN NaN NaN NaN 12 12.0 NaN NaN NaN NaN 12.0 NaN NaN NaN NaN 13 NaN NaN 13.0 NaN NaN NaN NaN 13.0 NaN NaN 14 NaN 14.0 NaN NaN NaN NaN 14.0 NaN NaN NaN 15 NaN NaN NaN 15.0 NaN NaN NaN NaN 15.0 NaN 16 NaN NaN NaN NaN 16.0 NaN NaN NaN NaN 16.0 17 NaN NaN 17.0 NaN NaN NaN NaN 17.0 NaN NaN 18 NaN NaN NaN NaN 18.0 NaN NaN NaN NaN 18.0 19 NaN NaN NaN 19.0 NaN NaN NaN NaN 19.0 NaN To coalesce this and only keep filled values you use df2 = pd.DataFrame() # propagates values and chooses first df2["AZI"] = df[azi_names].bfill(axis=1).iloc[:, 0] df2["DIS"] = df[dist_names].bfill(axis=1).iloc[:, 0] print(df2) to get a coalesced new df: AZI DIS 0 0.0 0.0 1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0 5 5.0 5.0 6 6.0 6.0 7 7.0 7.0 8 8.0 8.0 9 9.0 9.0 10 10.0 10.0 11 11.0 11.0 12 12.0 12.0 13 13.0 13.0 14 14.0 14.0 15 15.0 15.0 16 16.0 16.0 17 17.0 17.0 18 18.0 18.0 19 19.0 19.0 Attributation: inspired by Erfan's answer to Coalesce values from 2 columns into a single column in a pandas dataframe You may need to Replacing blank values (white space) with NaN in pandas for your shown data.
Pandas : How to concatenate or merge the groups using groupby function and populate single table or dataframe?
df = name description curve tenor rates IND 3M ZAR_3M 0.25 6.808000088 IND 2Y ZAR_3M 2 6.483012199 IND 3Y ZAR_3M 3 6.565002918 IND 4Y ZAR_3M 4 6.694129944 IND 5Y ZAR_3M 5 6.83951807 IND 3M CAD_OIS 0.25 1.738620043 BHU 6M CAD_OIS 0.5 1.718042016 IND 9M CAD_OIS 0.75 1.697247028 IND 1Y CAD_OIS 1 1.67719996 IND 18M CAD_OIS 1.5 1.631257057 IND 2Y CAD_3M 2 1.906309009 IND 3y CAD_3M 3 1.855569959 IND 4Y CAD_3M 4 1.830132961 BHU 5Y CAD_3M 5 1.817605019 BHU 6y CAD_3M 6 1.814880013 IND 7Y CAD_3M 7 1.821526051 BHU TND CZK_Curve 0.01 0.02 BHU 1WK CZK_Curve 0.03 0.0203 BHU 1M CZK_Curve 0.09 0.021 BHU 2M CZK_Curve 0.18 0.0212 BHU 3M CZK_Curve 0.26 0.0214 BHU 6M CZK_Curve 0.51 0.0212 BHU 9M CZK_Curve 0.76 0.02045 BHU 12M CZK_Curve 1.01 0.01985 BHU 2Y CZK_Curve 2.01 0.020033333 BHU 3Y CZK_Curve 3.02 0.018816667 BHU 4Y CZK_Curve 4.02 0.017666667 BHU 5Y CZK_Curve 5.02 0.016616667 BHU 6Y CZK_Curve 6.02 0.015766667 BHU 7Y CZK_Curve 7.02 0.015216667 BHU 8Y CZK_Curve 8.02 0.014616667 BHU 9Y CZK_Curve 9.02 0.014358333 Above is my dataframe(df) having 5 variables. I would like to populate the table based on 'curve' and rename the rates as curve name. Following is my expected output. I tried using groupby function to generate groups and concatenate side by side based on 'tenor'. But my code seems incomplete. Please suggest to how to produce the below output. df_tenor = df_tenor[['Tenor']].drop_duplicates() df_tenor = df_tenor.sort_values(by=['tenor']) gb = df.groupby('curve') df.rename(columns={'rates': str([df.curve.unique() for g in gb])}, inplace=True) df_final= pd.concat([g[1].merge(df_tenor, how='outer', on='Tenor') for g in gb], axis=1) df_final.to_csv('testconcat.csv', index = False)
Use ``pandas.pivot_table()``` pd.pivot_table(df, index='tenor', values='rates', columns='curve') Output curve CAD_3M CAD_OIS CZK_Curve ZAR_3M tenor 0.01 NaN NaN 0.020000 NaN 0.03 NaN NaN 0.020300 NaN 0.09 NaN NaN 0.021000 NaN 0.18 NaN NaN 0.021200 NaN 0.25 NaN 1.738620 NaN 6.808000 0.26 NaN NaN 0.021400 NaN 0.50 NaN 1.718042 NaN NaN 0.51 NaN NaN 0.021200 NaN 0.75 NaN 1.697247 NaN NaN 0.76 NaN NaN 0.020450 NaN 1.00 NaN 1.677200 NaN NaN 1.01 NaN NaN 0.019850 NaN 1.50 NaN 1.631257 NaN NaN 2.00 1.906309 NaN NaN 6.483012 2.01 NaN NaN 0.020033 NaN 3.00 1.855570 NaN NaN 6.565003 3.02 NaN NaN 0.018817 NaN 4.00 1.830133 NaN NaN 6.694130 4.02 NaN NaN 0.017667 NaN 5.00 1.817605 NaN NaN 6.839518 5.02 NaN NaN 0.016617 NaN 6.00 1.814880 NaN NaN NaN 6.02 NaN NaN 0.015767 NaN 7.00 1.821526 NaN NaN NaN 7.02 NaN NaN 0.015217 NaN 8.02 NaN NaN 0.014617 NaN 9.02 NaN NaN 0.014358 NaN
rolling moving average and std dev by multiple columns dynamically
I have a dataframe like this import pandas as pd import numpy as np raw_data = {'Country':['UK','UK','UK','UK','UK','UK','UK','UK','UK','UK','UK','UK','UK','UK','UK','US','US','US','US','US','US'], 'Product':['A','A','A','A','B','B','B','B','B','B','B','B','C','C','C','D','D','D','D','D','D'], 'Week': [1,2,3,4,1,2,3,4,5,6,7,8,1,2,3,1,2,3,4,5,6], 'val': [5,4,3,1,5,6,7,8,9,10,11,12,5,5,5,5,6,7,8,9,10] } df2 = pd.DataFrame(raw_data, columns = ['Country','Product','Week', 'val']) print(df2) i want to calculate moving average and std dev for val column by country and product..like 3 weeks,5 weeks ,7 weeks etc wanted dataframe: 'Contry', 'product','week',val', '3wks_avg' '3wks_std','5wks_avg',5wks,std'..etc
Like WenYoBen suggested, we can create a list of all the window sizes you want, and then dynamically create your wanted columns with GroupBy.rolling: weeks = [3, 5, 7] for week in weeks: df[[f'{week}wks_avg', f'{week}wks_std']] = ( df.groupby(['Country', 'Product']).rolling(window=week, on='Week')['val'] .agg(['mean', 'std']).reset_index(drop=True) ) Country Product Week val 3wks_avg 3wks_std 5wks_avg 5wks_std 7wks_avg 7wks_std 0 UK A 1 5 nan nan nan nan nan nan 1 UK A 2 4 nan nan nan nan nan nan 2 UK A 3 3 4.00 1.00 nan nan nan nan 3 UK A 4 1 2.67 1.53 nan nan nan nan 4 UK B 1 5 nan nan nan nan nan nan 5 UK B 2 6 nan nan nan nan nan nan 6 UK B 3 7 6.00 1.00 nan nan nan nan 7 UK B 4 8 7.00 1.00 nan nan nan nan 8 UK B 5 9 8.00 1.00 7.00 1.58 nan nan 9 UK B 6 10 9.00 1.00 8.00 1.58 nan nan 10 UK B 7 11 10.00 1.00 9.00 1.58 8.00 2.16 11 UK B 8 12 11.00 1.00 10.00 1.58 9.00 2.16 12 UK C 1 5 nan nan nan nan nan nan 13 UK C 2 5 nan nan nan nan nan nan 14 UK C 3 5 5.00 0.00 nan nan nan nan 15 US D 1 5 nan nan nan nan nan nan 16 US D 2 6 nan nan nan nan nan nan 17 US D 3 7 6.00 1.00 nan nan nan nan 18 US D 4 8 7.00 1.00 nan nan nan nan 19 US D 5 9 8.00 1.00 7.00 1.58 nan nan 20 US D 6 10 9.00 1.00 8.00 1.58 nan nan
This is how you would get the moving average for 3 weeks : df['3weeks_avg'] = list(df.groupby(['Country', 'Product']).rolling(3).mean()['val']) Apply the same principle for the other columns you want to compute.
IIUC, you may try this wks = ['Week_3', 'Week_5', 'Week_7'] df_calc = (df2.groupby(['Country', 'Product']).expanding().val .agg(['mean', 'std']).rename(lambda x: f'Week_{x+1}', level=-1) .query('ilevel_2 in #wks').unstack()) Out[246]: mean std Week_3 Week_5 Week_7 Week_3 Week_5 Week_7 Country Product UK A 4.0 NaN NaN 1.0 NaN NaN B NaN 5.0 6.0 NaN NaN 1.0
You will want to use a groupby-transform to get the rolling moments of your data. The following should compute what you are looking for: weeks = [3, 5, 7] # define weeks df2 = df2.sort_values('Week') # order by time for i in weeks: # loop through time intervals you want to compute df2['{}wks_avg'.format(i)] = df2.groupby(['Country', 'Product'])['val'].transform(lambda x: x.rolling(i).mean()) # i-week rolling mean df2['{}wks_std'.format(i)] = df2.groupby(['Country', 'Product'])['val'].transform(lambda x: x.rolling(i).std()) # i-week rolling std Here is what the resulting dataframe will look like. print(df2.dropna().head().to_string()) Country Product Week val 3wks_avg 3wks_std 5wks_avg 5wks_std 7wks_avg 7wks_std 17 US D 3 7 6.0 1.0 6.0 1.0 6.0 1.0 6 UK B 3 7 6.0 1.0 6.0 1.0 6.0 1.0 14 UK C 3 5 5.0 0.0 5.0 0.0 5.0 0.0 2 UK A 3 3 4.0 1.0 4.0 1.0 4.0 1.0 7 UK B 4 8 7.0 1.0 7.0 1.0 7.0 1.0
Convert upper triangular matrix to lower triangular matrix in Pandas Dataframe
I tried using transpose and adding some twists to it but it didn't workout Convert Upper: Data : 0 1 2 3 0 5 NaN NaN NaN 1 1 NaN NaN NaN 2 0.21 0.31 0.41 0.51 3 0.32 0.42 0.52 NaN 4 0.43 0.53 NaN NaN 5 0.54 NaN NaN Nan to: Data : 0 1 2 3 0 5 NaN NaN NaN 1 1 NaN NaN NaN 2 0.21 NaN NaN NaN 3 0.31 0.32 NaN NaN 4 0.41 0.42 0.43 NaN 5 0.51 0.52 0.53 0.54 without effecting the first two rows
I believe you need justify with sort with exclude first 2 rows: arr = justify(df.values[2:,:], invalid_val=np.nan, side='down', axis=0) df.values[2:,:] = np.sort(arr, axis=1) print (df) 0 1 2 3 0 5.00 NaN NaN NaN 1 1.00 NaN NaN NaN 2 0.21 NaN NaN NaN 3 0.31 0.32 NaN NaN 4 0.41 0.42 0.43 NaN 5 0.51 0.52 0.53 0.54
IIUC you can first index the dataframe from row 2 onwards and swap with the transpose, and then you can use justify so that all NaNs are at the top: df.iloc[2:,:] = df.iloc[2:,:].T.values pd.Dataframe(justify(df.values.astype(float), invalid_val=np.nan, side='down', axis=0)) 0 1 2 3 0 5 NaN NaN NaN 1 1 NaN NaN NaN 2 0.21 NaN NaN NaN 3 0.31 0.32 NaN NaN 4 0.41 0.42 0.43 NaN 5 0.51 0.52 0.53 0.54