Prevent NaN to become index and column in dataframe pivot - python

I have a dataframe which I extend to include values for all increments in 2 columns. Therefor NaN values are introduced, as expected and desired.
However, when I use pivot on this dataframe I'll get a row and column for NaN.
Can I prevent this when doing the pivot? If not, how can I drop a column named NaN?
Trying to drop it by calling [NaN],[nan] or ['NaN'] doesn't work.
Dropping the columns and rows where all values are NaN is not working in this case as the column headings and indexes are used for a seaborn heatmap plot, so eventhough all cell values are NaN it is still useful to have it as the index and key values are not NaN
Sample code;
import pandas as pd
import numpy as np
#generate dummy data
df = pd.DataFrame({'Y': np.random.randint(130,140,10),
'X': np.random.randint(5,10,10),
'Z': np.random.randint(0,25, size=10)})
df = df.round(1)
#create dataset for heatmap
#group by axis to plot
df = df.groupby(['X','Y']).sum().reset_index()
df = df.sort_values(by=['Y'])
dfY = pd.DataFrame({'Y':np.arange(min(df['Y']), max(df['Y']),1)})
dfX = pd.DataFrame({'X':np.arange(min(df['X']), max(df['X']),1)})
df = pd.merge(df,dfY, how='outer', on='Y')
df = pd.merge(df,dfX, how='outer', on='X')
df = df.round(1)
print(df)
#restructure for heatmap
data = df.pivot("Y","X","Z").sort_values(by=['Y'],ascending=False)
print(data)
Sample DataFrame before pivot:
X Y Z
0 5.0 132.0 0.0
1 5.0 135.0 20.0
2 5.0 137.0 17.0
3 7.0 132.0 15.0
4 7.0 133.0 3.0
5 6.0 133.0 30.0
6 6.0 135.0 22.0
7 6.0 138.0 16.0
8 9.0 135.0 9.0
9 NaN 134.0 NaN
10 NaN 136.0 NaN
11 8.0 NaN NaN
After pivot:
X NaN 5.0 6.0 7.0 8.0 9.0
Y
138.0 NaN NaN 16.0 NaN NaN NaN
137.0 NaN 17.0 NaN NaN NaN NaN
136.0 NaN NaN NaN NaN NaN NaN
135.0 NaN 20.0 22.0 NaN NaN 9.0
134.0 NaN NaN NaN NaN NaN NaN
133.0 NaN NaN 30.0 3.0 NaN NaN
132.0 NaN 0.0 NaN 15.0 NaN NaN
NaN NaN NaN NaN NaN NaN NaN
Desired output:
X 5.0 6.0 7.0 8.0 9.0
Y
138.0 NaN 16.0 NaN NaN NaN
137.0 17.0 NaN NaN NaN NaN
136.0 NaN NaN NaN NaN NaN
135.0 20.0 22.0 NaN NaN 9.0
134.0 NaN NaN NaN NaN NaN
133.0 NaN 30.0 3.0 NaN NaN
132.0 0.0 NaN 15.0 NaN NaN

For me working drop by missing value np.nan:
data = (df.pivot("Y","X","Z")
.sort_values(by=['Y'],ascending=False)
.drop(np.nan, axis=1)
.drop(np.nan))
Or:
data = df.pivot("Y","X","Z").sort_values(by=['Y'],ascending=False)
data = data.reindex(index=data.index.difference([np.nan]),
columns=data.columns.difference([np.nan]))

Related

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.

Can we add extra rows in pandas dataframe

import pandas as pd
data = {'id':[22.5, 24.5, 25.5],
'id_value':[100, 110, 120],
'new': [100, 110, 120]}
df = pd.DataFrame(data)
import numpy as np
Range = pd.DataFrame(data = np.arange(21, 30), columns=['id'])
df = pd.merge(df, Range, on =["id"], how ="outer")
can I add extra entries in "id"? without the last three line of the code?
Try append:
>>> df.append(pd.DataFrame(range(21, 30), columns=['id']))
id id_value new
0 22.5 100.0 100.0
1 24.5 110.0 110.0
2 25.5 120.0 120.0
0 21.0 NaN NaN
1 22.0 NaN NaN
2 23.0 NaN NaN
3 24.0 NaN NaN
4 25.0 NaN NaN
5 26.0 NaN NaN
6 27.0 NaN NaN
7 28.0 NaN NaN
8 29.0 NaN NaN
You can use append
df.append(pd.DataFrame({"id":np.arange(21, 30)}), ignore_index=True)
id id_value new
0 22.5 100.0 100.0
1 24.5 110.0 110.0
2 25.5 120.0 120.0
3 21.0 NaN NaN
4 22.0 NaN NaN
5 23.0 NaN NaN
6 24.0 NaN NaN
7 25.0 NaN NaN
8 26.0 NaN NaN
9 27.0 NaN NaN
10 28.0 NaN NaN
11 29.0 NaN NaN

Convert two pandas rows into one

I want to convert below dataframe,
ID TYPE A B
0 1 MISSING 0.0 0.0
1 2 1T 1.0 2.0
2 2 2T 3.0 4.0
3 3 MISSING 0.0 0.0
4 4 2T 10.0 4.0
5 5 CBN 15.0 20.0
6 5 DSV 25.0 35.0
to:
ID MISSING_A MISSING_B 1T_A 1T_B 2T_A 2T_B CBN_A CBN_B DSV_A DSV_B
0 1 0.0 0.0 NaN NaN NaN NaN NaN NaN NaN NaN
1 2 NaN NaN 1.0 2.0 3.0 4.0 NaN NaN NaN NaN
3 3 0.0 0.0 NaN NaN NaN NaN NaN NaN NaN NaN
4 4 10.0 4.0 NaN NaN 10.0 4.0 NaN NaN NaN NaN
5 5 NaN NaN NaN NaN NaN NaN 15.0 20.0 25.0 35.0
For IDs with multiple types, multiple rows for A and B to merge into one row as shown above.
You are looking for a pivot, which will end up giving you a multi-index. You'll need to join those columns to get the suffix you are looking for.
df = df.pivot(index='ID',columns='TYPE', values=['A','B'])
df.columns = ['_'.join(reversed(col)).strip() for col in df.columns.values]
df.reset_index()

ReArrange Pandas DataFrame date columns in date order

I have a pandas dataframe that summarises sales by calendar month & outputs something like:
Month level_0 UNIQUE_ID 102018 112018 12018 122017 122018 22018 32018 42018 52018 62018 72018 82018 92018
0 SOLD_QUANTITY 01 3692.0 5182.0 3223.0 1292.0 2466.0 2396.0 2242.0 2217.0 3590.0 2593.0 1665.0 3371.0 3069.0
1 SOLD_QUANTITY 011 3.0 6.0 NaN NaN 7.0 5.0 2.0 1.0 5.0 NaN 1.0 1.0 3.0
2 SOLD_QUANTITY 02 370.0 130.0 NaN NaN 200.0 NaN NaN 269.0 202.0 NaN 201.0 125.0 360.0
3 SOLD_QUANTITY 03 2.0 6.0 NaN NaN 2.0 1.0 NaN 6.0 11.0 9.0 2.0 3.0 5.0
4 SOLD_QUANTITY 08 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 175.0 NaN NaN
I want to be able to programmatically re-arrange the column headers in ascending date order (eg starting 122017, 12018, 22018...). I need to do it in a way that is programmatic as every way the report runs, it will be a different list of months as it runs every month for last 365 days.
The index data type:
Index(['level_0', 'UNIQUE_ID', '102018', '112018', '12018', '122017', '122018',
'22018', '32018', '42018', '52018', '62018', '72018', '82018', '92018'],
dtype='object', name='Month')
Use set_index for only dates columns, convert them to datetimes and get order positions by argsort, then change ordering with iloc:
df = df.set_index(['level_0','UNIQUE_ID'])
df = df.iloc[:, pd.to_datetime(df.columns, format='%m%Y').argsort()].reset_index()
print (df)
level_0 UNIQUE_ID 122017 12018 22018 32018 42018 52018 \
0 SOLD_QUANTITY 1 1292.0 3223.0 2396.0 2242.0 2217.0 3590.0
1 SOLD_QUANTITY 11 NaN NaN 5.0 2.0 1.0 5.0
2 SOLD_QUANTITY 2 NaN NaN NaN NaN 269.0 202.0
3 SOLD_QUANTITY 3 NaN NaN 1.0 NaN 6.0 11.0
4 SOLD_QUANTITY 8 NaN NaN NaN NaN NaN NaN
62018 72018 82018 92018 102018 112018 122018
0 2593.0 1665.0 3371.0 3069.0 3692.0 5182.0 2466.0
1 NaN 1.0 1.0 3.0 3.0 6.0 7.0
2 NaN 201.0 125.0 360.0 370.0 130.0 200.0
3 9.0 2.0 3.0 5.0 2.0 6.0 2.0
4 NaN 175.0 NaN NaN NaN NaN NaN
Another idea is create month period index by DatetimeIndex.to_period, so is possible use sort_index:
df = df.set_index(['level_0','UNIQUE_ID'])
df.columns = pd.to_datetime(df.columns, format='%m%Y').to_period('m')
#alternative for convert to datetimes
#df.columns = pd.to_datetime(df.columns, format='%m%Y')
df = df.sort_index(axis=1).reset_index()
print (df)
level_0 UNIQUE_ID 2017-12 2018-01 2018-02 2018-03 2018-04 \
0 SOLD_QUANTITY 1 1292.0 3223.0 2396.0 2242.0 2217.0
1 SOLD_QUANTITY 11 NaN NaN 5.0 2.0 1.0
2 SOLD_QUANTITY 2 NaN NaN NaN NaN 269.0
3 SOLD_QUANTITY 3 NaN NaN 1.0 NaN 6.0
4 SOLD_QUANTITY 8 NaN NaN NaN NaN NaN
2018-05 2018-06 2018-07 2018-08 2018-09 2018-10 2018-11 2018-12
0 3590.0 2593.0 1665.0 3371.0 3069.0 3692.0 5182.0 2466.0
1 5.0 NaN 1.0 1.0 3.0 3.0 6.0 7.0
2 202.0 NaN 201.0 125.0 360.0 370.0 130.0 200.0
3 11.0 9.0 2.0 3.0 5.0 2.0 6.0 2.0
4 NaN NaN 175.0 NaN NaN NaN NaN NaN

Pandas long to wide with values filled based on answer

I am trying to convert a dataframe from long to wide, but Im not sure how to convert it to the format below. What am I missing?
d = {'vote': [100, 50,1,23,55,67,89,44],
'vote2': [10, 2,18,26,77,99,9,40],
'ballot1': ['a','b','a','a','b','a','a','b'],
'voteId':[1,2,3,4,5,6,7,8]}
df1=pd.DataFrame(d)
#########################################################
dftemp=df1
#####FORMATTING DATA
dftemp=pd.DataFrame(dftemp.reset_index())
dflw= dftemp.set_index(['voteId','vote','ballot1'])
dflw=dflw.unstack()
dflw.columns = dflw.columns.droplevel(0).rename('')
dflw=pd.DataFrame(dflw)
print(dflw)
MY CURRENT OUTPUT:
a b a b
voteId vote
1 100 0.0 NaN 10.0 NaN
2 50 NaN 1.0 NaN 2.0
GOAL:
voteid (ballot1=a)vote (ballot1=b)vote (ballot1=a)vote2 (ballot1=b)vote2
1 100 NaN 10 NaN
2 NaN 50 NaN 2
I am starting from df1
s=df1.set_index(['voteId','ballot1']).unstack()
s.columns=s.columns.map('(ballot1={0[1]}){0[0]}'.format)
s
Out[1120]:
(ballot1=a)vote (ballot1=b)vote (ballot1=a)vote2 (ballot1=b)vote2
voteId
1 100.0 NaN 10.0 NaN
2 NaN 50.0 NaN 2.0
3 1.0 NaN 18.0 NaN
4 23.0 NaN 26.0 NaN
5 NaN 55.0 NaN 77.0
6 67.0 NaN 99.0 NaN
7 89.0 NaN 9.0 NaN
8 NaN 44.0 NaN 40.0

Categories

Resources