Adding missing data to Dataframe - pandas

Hello I have a dataframe that looks like this
Year month pop slope intercept
2020 2 10 5.8 -3.2
2020 3 15 5.8 -3.2
2020 4 17 5.8 -3.2
2020 9 50 5.8 -3.2
2021 1 5 8 -8.5
2021 5 20 8 -8.5
2021 10 75 8 -8.5
I would like to add all of the missing months so that I can calculate a predicted population for every month of the year with the following code.
df['pred_pop'] = (df['month'] * df['slope']) + df['intercept']
I have the following code that seems to work but I was looking to see if there was an easier way to accomplish this without creating a new column.
df['new_month'] = df.apply(lambda x: [1,2,3,4,5,6,7,8,9,10,11,12], axis=1)
df = df.explode('new_month')

Months = [*range(1, 13, 1)]
mux = pd.MultiIndex.from_product([df['Year'].unique(), Months], names=('Year','month'))
df = df.set_index(['Year','month']).reindex(mux).swaplevel(0,1).reset_index()
print(df)
output:
month Year pop slope intercept
0 1 2020 NaN NaN NaN
1 2 2020 10.0 5.8 -3.2
2 3 2020 15.0 5.8 -3.2
3 4 2020 17.0 5.8 -3.2
4 5 2020 NaN NaN NaN
5 6 2020 NaN NaN NaN
6 7 2020 NaN NaN NaN
7 8 2020 NaN NaN NaN
8 9 2020 50.0 5.8 -3.2
9 10 2020 NaN NaN NaN
10 11 2020 NaN NaN NaN
11 12 2020 NaN NaN NaN
12 1 2021 5.0 8.0 -8.5
13 2 2021 NaN NaN NaN
14 3 2021 NaN NaN NaN
15 4 2021 NaN NaN NaN
16 5 2021 20.0 8.0 -8.5
17 6 2021 NaN NaN NaN
18 7 2021 NaN NaN NaN
19 8 2021 NaN NaN NaN
20 9 2021 NaN NaN NaN
21 10 2021 75.0 8.0 -8.5
22 11 2021 NaN NaN NaN
23 12 2021 NaN NaN NaN

You can try
out = df.pivot('month','Year').reindex(range(1,12+1)).stack(dropna=False).reset_index()
And next step is to fill the nan

#pip install git+https://github.com/pyjanitor-devs/pyjanitor.git
import pandas as pd
import janitor
df.complete('Year', dict(month=range(1,13)), sort = True)
Year month pop slope intercept
0 2020 1 NaN NaN NaN
1 2020 2 10.0 5.8 -3.2
2 2020 3 15.0 5.8 -3.2
3 2020 4 17.0 5.8 -3.2
4 2020 5 NaN NaN NaN
5 2020 6 NaN NaN NaN
6 2020 7 NaN NaN NaN
7 2020 8 NaN NaN NaN
8 2020 9 50.0 5.8 -3.2
9 2020 10 NaN NaN NaN
10 2020 11 NaN NaN NaN
11 2020 12 NaN NaN NaN
12 2021 1 5.0 8.0 -8.5
13 2021 2 NaN NaN NaN
14 2021 3 NaN NaN NaN
15 2021 4 NaN NaN NaN
16 2021 5 20.0 8.0 -8.5
17 2021 6 NaN NaN NaN
18 2021 7 NaN NaN NaN
19 2021 8 NaN NaN NaN
20 2021 9 NaN NaN NaN
21 2021 10 75.0 8.0 -8.5
22 2021 11 NaN NaN NaN
23 2021 12 NaN NaN NaN
This uses the complete function from pyjanitor., which can be helpful in explicitly exposing missing rows; in this case, we build a new dataframe from the pairing of the Year column, and a dictionary of the month column and all the months in a year

Related

Use condition in a dataframe to replace values in another dataframe with nan

I have a dataframe that contains concentration values for a set of samples as follows:
Sample
Ethanol
Acetone
Formaldehyde
Methane
A
20
20
20
20
A
30
23
20
nan
A
20
23
nan
nan
A
nan
20
nan
nan
B
21
46
87
54
B
23
74
nan
54
B
23
67
nan
53
B
23
nan
nan
33
C
23
nan
nan
66
C
22
nan
nan
88
C
22
nan
nan
90
C
22
nan
nan
88
I have second dataframe that contains the proportion of concentration values that are not missing in the first dataframe:
Sample
Ethanol
Acetone
Formaldehyde
Methane
A
0.75
1
0.5
0.25
B
1
0.75
0.25
1
C
1
0
0
1
I would like to replace value in the first dataframe with nan when the condition in the second dataframe is 0.5 or less. Hence, the resulting dataframe would look like that below. Any help would be great!
Sample
Ethanol
Acetone
Formaldehyde
Methane
A
20
20
nan
nan
A
30
23
nan
nan
A
20
23
nan
nan
A
nan
20
nan
nan
B
21
46
nan
54
B
23
74
nan
54
B
23
67
nan
53
B
23
nan
nan
33
C
23
nan
nan
66
C
22
nan
nan
88
C
22
nan
nan
90
C
22
nan
nan
88
Is it what your are looking for:
>>> df2.set_index('Sample').mask(lambda x: x <= 0.5) \
.mul(df1.set_index('Sample')).reset_index()
Sample Ethanol Acetone Formaldehyde Methane
0 A 15.0 20.00 NaN NaN
1 A 22.5 23.00 NaN NaN
2 A 15.0 23.00 NaN NaN
3 A NaN 20.00 NaN NaN
4 B 21.0 34.50 NaN 54.0
5 B 23.0 55.50 NaN 54.0
6 B 23.0 50.25 NaN 53.0
7 B 23.0 NaN NaN 33.0
8 C 23.0 NaN NaN 66.0
9 C 22.0 NaN NaN 88.0
10 C 22.0 NaN NaN 90.0
11 C 22.0 NaN NaN 88.0

Pandas - get rid of NaNs based on column values

I need to get rid of NaNs here, consolidating my Dataframe based on my 'id' column, which has a fixed set of values, always.
id home_5 home_4 \
0 277.0 21.231579 NaN
1 280.0 20.689474 NaN
2 282.0 16.152632 NaN
3 262.0 16.026316 NaN
4 284.0 12.642105 NaN
5 356.0 12.163158 NaN
6 293.0 11.715789 NaN
7 263.0 11.452632 NaN
8 276.0 11.405263 NaN
9 265.0 10.505263 NaN
10 266.0 10.363158 NaN
11 275.0 10.010526 NaN
12 290.0 9.242105 NaN
13 267.0 9.063158 NaN
14 292.0 8.600000 NaN
15 373.0 8.357895 NaN
16 264.0 7.584211 NaN
17 354.0 6.989474 NaN
18 285.0 6.168421 NaN
19 294.0 5.784211 NaN
0 285.0 NaN 23.947368
1 354.0 NaN 16.100000
2 266.0 NaN 15.047368
3 262.0 NaN 14.857895
4 264.0 NaN 12.615789
5 282.0 NaN 12.363158
6 275.0 NaN 12.089474
7 267.0 NaN 10.615789
8 373.0 NaN 10.452632
9 265.0 NaN 9.810526
10 284.0 NaN 9.568421
11 294.0 NaN 9.273684
12 280.0 NaN 8.910526
13 277.0 NaN 8.536842
14 293.0 NaN 8.257895
15 292.0 NaN 7.784211
16 263.0 NaN 7.626316
17 276.0 NaN 7.026316
18 290.0 NaN 6.073684
19 356.0 NaN 4.136842
Desired result:
id home_5 home_4 \
0 277.0 21.231579 8.536842
1 280.0 20.689474 8.910526
2 282.0 16.152632 12.363158
3 262.0 16.026316 14.857895
4 284.0 12.642105 9.568421
5 356.0 12.163158 4.136842
6 293.0 11.715789 8.257895
7 263.0 11.452632 7.626316
8 276.0 11.405263 7.026316
9 265.0 10.505263 9.810526
10 266.0 10.363158 15.047368
11 275.0 10.010526 12.089474
12 290.0 9.242105 6.073684
13 267.0 9.063158 10.615789
14 292.0 8.600000 7.784211
15 373.0 8.357895 10.452632
16 264.0 7.584211 12.615789
17 354.0 6.989474 16.100000
18 285.0 6.168421 23.947368
19 294.0 5.784211 9.273684
How do I achieve this?
Just fix your df
out = df.groupby('id').first()

How to remove periods of time in a dataframe?

I have this df:
CODE YEAR MONTH DAY TMAX TMIN PP BAD PERIOD 1 BAD PERIOD 2
9984 000130 1991 1 1 32.6 23.4 0.0 1991 1998
9985 000130 1991 1 2 31.2 22.4 0.0 NaN NaN
9986 000130 1991 1 3 32.0 NaN 0.0 NaN NaN
9987 000130 1991 1 4 32.2 23.0 0.0 NaN NaN
9988 000130 1991 1 5 30.5 22.0 0.0 NaN NaN
... ... ... ... ... ... ...
20118 000130 2018 9 30 31.8 21.2 NaN NaN NaN
30028 000132 1991 1 1 35.2 NaN 0.0 2005 2010
30029 000132 1991 1 2 34.6 NaN 0.0 NaN NaN
30030 000132 1991 1 3 35.8 NaN 0.0 NaN NaN
30031 000132 1991 1 4 34.8 NaN 0.0 NaN NaN
... ... ... ... ... ... ...
50027 000132 2019 10 5 36.5 NaN 13.1 NaN NaN
50028 000133 1991 1 1 36.2 NaN 0.0 1991 2010
50029 000133 1991 1 2 36.6 NaN 0.0 NaN NaN
50030 000133 1991 1 3 36.8 NaN 5.0 NaN NaN
50031 000133 1991 1 4 36.8 NaN 0.0 NaN NaN
... ... ... ... ... ... ...
54456 000133 2019 10 5 36.5 NaN 12.1 NaN NaN
I want to change the values ​​of the columns TMAX TMIN and PP to NaN, only of the periods specified in Bad Period 1 and Bad period 2 AND ONLY IN THEIR RESPECTIVE CODE. For example if I have Bad Period 1 equal to 1991 and Bad period 2 equal to 1998 I want all the values of TMAX, TMIN and PP that have code 000130 have NaN values since 1991 (bad period 1) to 1998 (bad period 2). I have 371 unique CODES in CODE column so i might use df.groupby("CODE").
Expected result after the change:
CODE YEAR MONTH DAY TMAX TMIN PP BAD PERIOD 1 BAD PERIOD 2
9984 000130 1991 1 1 NaN NaN NaN 1991 1998
9985 000130 1991 1 2 NaN NaN NaN NaN NaN
9986 000130 1991 1 3 NaN NaN NaN NaN NaN
9987 000130 1991 1 4 NaN NaN NaN NaN NaN
9988 000130 1991 1 5 NaN NaN NaN NaN NaN
... ... ... ... ... ... ...
20118 000130 2018 9 30 31.8 21.2 NaN NaN NaN
30028 000132 1991 1 1 35.2 NaN 0.0 2005 2010
30029 000132 1991 1 2 34.6 NaN 0.0 NaN NaN
30030 000132 1991 1 3 35.8 NaN 0.0 NaN NaN
30031 000132 1991 1 4 34.8 NaN 0.0 NaN NaN
... ... ... ... ... ... ...
50027 000132 2019 10 5 36.5 NaN 13.1 NaN NaN
50028 000133 1991 1 1 NaN NaN NaN 1991 2010
50029 000133 1991 1 2 NaN NaN NaN NaN NaN
50030 000133 1991 1 3 NaN NaN NaN NaN NaN
50031 000133 1991 1 4 NaN NaN NaN NaN NaN
... ... ... ... ... ... ...
54456 000133 2019 10 5 36.5 NaN 12.1 NaN NaN
you can propagate the values in your bad columns with ffill, if the non nan values are always at the first row per group of CODE and your data is ordered per CODE. If not, with groupby.transform and first. Then use mask to replace by nan where the YEAR is between your two bad columns once filled with the wanted value.
df_ = df[['BAD_1', 'BAD_2']].ffill()
#or more flexible df_ = df.groupby("CODE")[['BAD_1', 'BAD_2']].transform('first')
cols = ['TMAX', 'TMIN', 'PP']
df[cols] = df[cols].mask(df['YEAR'].ge(df_['BAD_1'])
& df['YEAR'].le(df_['BAD_2']))
print(df)
CODE YEAR MONTH DAY TMAX TMIN PP BAD_1 BAD_2
9984 130 1991 1 1 NaN NaN NaN 1991.0 1998.0
9985 130 1991 1 2 NaN NaN NaN NaN NaN
9986 130 1991 1 3 NaN NaN NaN NaN NaN
9987 130 1991 1 4 NaN NaN NaN NaN NaN
9988 130 1991 1 5 NaN NaN NaN NaN NaN
20118 130 2018 9 30 31.8 21.2 NaN NaN NaN
30028 132 1991 1 1 35.2 NaN 0.0 2005.0 2010.0
30029 132 1991 1 2 34.6 NaN 0.0 NaN NaN
30030 132 1991 1 3 35.8 NaN 0.0 NaN NaN
30031 132 1991 1 4 34.8 NaN 0.0 NaN NaN
50027 132 2019 10 5 36.5 NaN 13.1 NaN NaN
50028 133 1991 1 1 NaN NaN NaN 1991.0 2010.0
50029 133 1991 1 2 NaN NaN NaN NaN NaN
50030 133 1991 1 3 NaN NaN NaN NaN NaN
50031 133 1991 1 4 NaN NaN NaN NaN NaN
54456 133 2019 10 5 36.5 NaN 12.1 NaN NaN

How can I unpivot data with multiple columns and multiple variables in pandas?

How can I unpivot data with multiple columns and multiple variables in pandas?
my input:
And desire output:
Remove the Na, add a column name, and 'append()' the value to an empty 'DataFrame'.
product ene ene_total feb feb_total mar mar_total
0 A NaN NaN 2.0 218.75 NaN NaN
1 B NaN NaN 1.0 27.40 NaN NaN
2 C NaN NaN NaN NaN 24.0 1530.00
3 D NaN NaN NaN NaN 24.0 1102.50
4 E NaN NaN NaN NaN 12.0 206.79
5 F NaN NaN NaN NaN 24.0 317.14
6 G 6.0 98.89 NaN NaN NaN NaN
7 H NaN NaN NaN NaN 24.0 385.29
8 I NaN NaN NaN NaN 25.0 895.98
new_df = pd.DataFrame(index=[], columns=[0,1,2,3])
for i in range(len(df)):
tmp = df.iloc[i].dropna()
new_df = new_df.append(pd.Series([tmp.index[1],tmp[0],tmp[1],tmp[2]]), ignore_index=True)
new_df.rename(columns={0:'period', 2:'unit', 3:'total'}).set_index(1)
period unit total
1
A feb 2.0 218.75
B feb 1.0 27.40
C mar 24.0 1530.00
D mar 24.0 1102.50
E mar 12.0 206.79
F mar 24.0 317.14
G ene 6.0 98.89
H mar 24.0 385.29
I mar 25.0 895.98

Compute a sequential rolling mean in pandas as array function?

I am trying to calculate a rolling mean on dataframe with NaNs in pandas, but pandas seems to reset the window when it meets a NaN, hears some code as an example...
import numpy as np
from pandas import *
foo = DataFrame(np.arange(0.0,13.0))
foo['1'] = np.arange(13.0,26.0)
foo.ix[4:6,0] = np.nan
foo.ix[4:7,1] = np.nan
bar = rolling_mean(foo, 4)
gives the rolling mean that resets the window after each NaN's, not just skipping out the NaNs
bar =
0 1
0 NaN NaN
1 NaN NaN
2 NaN NaN
3 1.5 14.5
4 NaN NaN
5 NaN NaN
6 NaN NaN
7 NaN NaN
8 NaN NaN
9 NaN NaN
10 8.5 NaN
11 9.5 22.5
12 10.5 23.5
I have found an ugly iter/ dropna() work around that gives the right answer
def sparse_rolling_mean(df_data, window):
...: f_data = DataFrame(np.nan,index=df_data.index, columns=df_data.columns)
...: for i in f_data.columns:
...: f_data.ix[:,i] = rolling_mean(df_data.ix[:,i].dropna(),window)
...: return f_data
bar = sparse_rolling_mean(foo,4)
bar
0 1
0 NaN NaN
1 NaN NaN
2 NaN NaN
3 1.50 14.5
4 NaN NaN
5 NaN NaN
6 NaN NaN
7 3.25 NaN
8 5.00 16.5
9 6.75 18.5
10 8.50 20.5
11 9.50 22.5
12 10.50 23.5
does anyone know if it is possible to do this as an array function ?
many thanks in advance.
you may do:
>>> def sparse_rolling_mean(ts, window):
... return rolling_mean(ts.dropna(), window).reindex_like(ts)
...
>>> foo.apply(sparse_rolling_mean, args=(4,))
0 1
0 NaN NaN
1 NaN NaN
2 NaN NaN
3 1.50 14.5
4 NaN NaN
5 NaN NaN
6 NaN NaN
7 3.25 NaN
8 5.00 16.5
9 6.75 18.5
10 8.50 20.5
11 9.50 22.5
12 10.50 23.5
[13 rows x 2 columns]
you can control what get's naned out with the min_periods arg
In [12]: rolling_mean(foo, 4,min_periods=1)
Out[12]:
0 1
0 0.0 13.0
1 0.5 13.5
2 1.0 14.0
3 1.5 14.5
4 2.0 15.0
5 2.5 15.5
6 3.0 16.0
7 7.0 NaN
8 7.5 21.0
9 8.0 21.5
10 8.5 22.0
11 9.5 22.5
12 10.5 23.5
[13 rows x 2 columns]
You can do this if you want results, except when the original was nan
In [27]: rolling_mean(foo, 4,min_periods=1)[foo.notnull()]
Out[27]:
0 1
0 0.0 13.0
1 0.5 13.5
2 1.0 14.0
3 1.5 14.5
4 NaN NaN
5 NaN NaN
6 NaN NaN
7 7.0 NaN
8 7.5 21.0
9 8.0 21.5
10 8.5 22.0
11 9.5 22.5
12 10.5 23.5
[13 rows x 2 columns]
Your expected are a bit odd, as the first 3 rows should have values.