Pandas - get rid of NaNs based on column values - pandas

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()

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

Adding missing data to Dataframe

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

How to count months with at least 1 non NaN value?

I have this df:
CODE YEAR MONTH DAY TMAX TMIN PP
0 130 1991 1 1 32.6 23.4 0.0
1 130 1991 1 2 31.2 22.4 0.0
2 130 1991 1 3 32.0 NaN 0.0
3 130 1991 1 4 32.2 23.0 0.0
4 130 1991 1 5 30.5 22.0 0.0
... ... ... ... ... ... ...
20118 130 2018 9 30 31.8 21.2 NaN
30028 132 1991 1 1 35.2 NaN 0.0
30029 132 1991 1 2 34.6 NaN 0.0
30030 132 1991 1 3 35.8 NaN 0.0
30031 132 1991 1 4 34.8 NaN 0.0
... ... ... ... ... ... ...
45000 132 2019 10 5 35.5 NaN 21.1
46500 133 1991 1 1 35.5 NaN 21.1
I need to count months that have at least 1 non NaN value in TMAX,TMIN and PP columns. If the month have all nan values that month doesn't count. I need to do this by each CODE.
Expected value:
CODE YEAR MONTH DAY TMAX TMIN PP JANUARY_TMAX FEBRUARY_TMAX MARCH_TMAX APRIL_TMAX etc
130 1991 1 1 32.6 23.4 0 23 25 22 27 …
130 1991 1 2 31.2 22.4 0 NaN NaN NaN NaN NaN
130 1991 1 3 32 NaN 0 NaN NaN NaN NaN NaN
130 1991 1 4 32.2 23 0 NaN NaN NaN NaN NaN
130 1991 1 5 30.5 22 0 NaN NaN NaN NaN NaN
... ... ... ... ... ... ... NaN NaN NaN NaN NaN
130 2018 9 30 31.8 21.2 NaN NaN NaN NaN NaN NaN
132 1991 1 1 35.2 NaN 0 21 23 22 22 …
132 1991 1 2 34.6 NaN 0 NaN NaN NaN NaN NaN
132 1991 1 3 35.8 NaN 0 NaN NaN NaN NaN NaN
132 1991 1 4 34.8 NaN 0 NaN NaN NaN NaN NaN
... ... ... ... ... ... ... NaN NaN NaN NaN NaN
132 2019 1 1 35.5 NaN 21.1 NaN NaN NaN NaN NaN
... ... ... ... ... ... ... NaN NaN NaN NaN NaN
133 1991 1 1 35.5 NaN 21.1 25 22 22 21 …
... ... ... ... ... ... ... NaN NaN NaN NaN NaN
For example: In code 130 for TMAX column, i have 23 Januarys that have at least 1 non NaN value, i have 25 Februarys that have at least 1 non NaN value, etc.
Would you mind to help me? Thanks in advance.
This may not be super efficient, but here is how you can do it for one of columns, TMAX in this case. Just repeat the process for the other columns.
# Count occurrences of each month when TMAX is not null
tmax_cts_long = df[df.TMAX.notnull()].drop_duplicates(subset=['CODE', 'YEAR', 'MONTH']).groupby(['CODE', 'MONTH']).size().reset_index(name='COUNT')
# Transpose the long table of counts to wide format
tmax_cts_wide = tmax_cts_long.pivot(index='CODE', columns='MONTH', values='COUNT')
# Merge table of counts with the original dataframe
final_df = df.merge(tmax_cts_wide, on='CODE', how='left')
# Replace values in new columns in all rows after the first row with NaN
mask = final_df.index.isin(df.groupby(['CODE', 'MONTH']).head(1).index)
final_df.loc[~mask, [col for col in final_df.columns if isinstance(col, int)]] = None
# Rename new columns to follow the desired naming format
mon_dict = {1: 'JANUARY', 2: 'FEBRUARY', ...}
tmax_mon_dict = {k: v + '_TMAX' for k, v in mon_dict.items()}
final_df.rename(columns=tmax_mon_dict, inplace=True)

expand mid year values to month in pandas

following from expand year values to month in pandas
I have:
pd.DataFrame({'comp':['a','b'], 'period':['20180331','20171231'],'value':[12,24]})
comp period value
0 a 20180331 12
1 b 20171231 24
and would like to extrapolate to 201701 to 201812 inclusive. The value should be spread out for the 12 months preceding the period.
comp yyymm value
a 201701 na
a 201702 na
...
a 201705 12
a 201706 12
...
a 201803 12
a 201804 na
b 201701 24
...
b 201712 24
b 201801 na
...
Use:
#create month periods with min and max value
r = pd.period_range('2017-01', '2018-12', freq='m')
#convert column to period
df['period'] = pd.to_datetime(df['period']).dt.to_period('m')
#create MultiIndex for add all possible values
mux = pd.MultiIndex.from_product([df['comp'], r], names=('comp','period'))
#reindex for append values
df = df.set_index(['comp','period'])['value'].reindex(mux).reset_index()
#back filling by 11 values of missing values per groups
df['new'] = df.groupby('comp')['value'].bfill(limit=11)
print (df)
comp period value new
0 a 2017-01 NaN NaN
1 a 2017-02 NaN NaN
2 a 2017-03 NaN NaN
3 a 2017-04 NaN 12.0
4 a 2017-05 NaN 12.0
...
...
10 a 2017-11 NaN 12.0
11 a 2017-12 NaN 12.0
12 a 2018-01 NaN 12.0
13 a 2018-02 NaN 12.0
14 a 2018-03 12.0 12.0
15 a 2018-04 NaN NaN
16 a 2018-05 NaN NaN
17 a 2018-06 NaN NaN
18 a 2018-07 NaN NaN
19 a 2018-08 NaN NaN
20 a 2018-09 NaN NaN
21 a 2018-10 NaN NaN
22 a 2018-11 NaN NaN
23 a 2018-12 NaN NaN
24 b 2017-01 NaN 24.0
25 b 2017-02 NaN 24.0
26 b 2017-03 NaN 24.0
...
...
32 b 2017-09 NaN 24.0
33 b 2017-10 NaN 24.0
34 b 2017-11 NaN 24.0
35 b 2017-12 24.0 24.0
36 b 2018-01 NaN NaN
37 b 2018-02 NaN NaN
38 b 2018-03 NaN NaN
...
...
44 b 2018-09 NaN NaN
45 b 2018-10 NaN NaN
46 b 2018-11 NaN NaN
47 b 2018-12 NaN NaN
See if this works:
dftime = pd.DataFrame(pd.date_range('20170101','20181231'), columns=['dt']).apply(lambda x: x.dt.strftime('%Y-%m'), axis=1) # Populating full range including dates
dftime = dftime.assign(dt=dftime.dt.drop_duplicates().reset_index(drop=True)).dropna() # Dropping duplicates from above range
df['dt'] = pd.to_datetime(df.period).apply(lambda x: x.strftime('%Y-%m')) # Adding column for merging purpose
target = df.groupby('comp').apply(lambda x: dftime.merge(x[['comp','dt','value']], on='dt', how='left').fillna({'comp':x.comp.unique()[0]})).reset_index(drop=True) # Populating data for each company
This gives desired output:
print(target)
dt comp value
0 2017-01 a NaN
1 2017-02 a NaN
2 2017-03 a NaN
3 2017-04 a NaN
4 2017-05 a NaN
5 2017-06 a NaN
6 2017-07 a NaN
and so on.

Concatenating dataframe that have different number of rows

I have a dataframe df = df[['A', 'B', 'C']] with 3 columns and 2000 rows
Then I have another set of data with only 200 rows
How can I add this into df['D'] such that this 200 rows will only appear as the tail of the 2000 rows?
So that from row 0-1800 for df['D'] it will be NaN and then 1801 to 2000 will be the values
Been trying various ways without success... thank you
data with 200 rows in this format
[[ 0.43628979]
[ 0.43454027]
[ 0.43552566]
[ 0.43542767]
[ 0.43331838]
...
I believe you need join with changing index by last index values of df1:
np.random.seed(100)
df1 = pd.DataFrame(np.random.randint(10, size=(20,3)), columns=list('ABC'))
print (df1)
A B C
0 8 8 3
1 7 7 0
2 4 2 5
3 2 2 2
4 1 0 8
5 4 0 9
6 6 2 4
7 1 5 3
8 4 4 3
9 7 1 1
10 7 7 0
11 2 9 9
12 3 2 5
13 8 1 0
14 7 6 2
15 0 8 2
16 5 1 8
17 1 5 4
18 2 8 3
19 5 0 9
df2 = pd.DataFrame(np.random.randint(10, size=(2,5)), columns=list('werty'))
print (df2)
w e r t y
0 3 6 3 4 7
1 6 3 9 0 4
df2.index = df1.index[-len(df2.index):]
df = df1.join(df2)
print (df)
A B C w e r t y
0 8 8 3 NaN NaN NaN NaN NaN
1 7 7 0 NaN NaN NaN NaN NaN
2 4 2 5 NaN NaN NaN NaN NaN
3 2 2 2 NaN NaN NaN NaN NaN
4 1 0 8 NaN NaN NaN NaN NaN
5 4 0 9 NaN NaN NaN NaN NaN
6 6 2 4 NaN NaN NaN NaN NaN
7 1 5 3 NaN NaN NaN NaN NaN
8 4 4 3 NaN NaN NaN NaN NaN
9 7 1 1 NaN NaN NaN NaN NaN
10 7 7 0 NaN NaN NaN NaN NaN
11 2 9 9 NaN NaN NaN NaN NaN
12 3 2 5 NaN NaN NaN NaN NaN
13 8 1 0 NaN NaN NaN NaN NaN
14 7 6 2 NaN NaN NaN NaN NaN
15 0 8 2 NaN NaN NaN NaN NaN
16 5 1 8 NaN NaN NaN NaN NaN
17 1 5 4 NaN NaN NaN NaN NaN
18 2 8 3 3.0 6.0 3.0 4.0 7.0
19 5 0 9 6.0 3.0 9.0 0.0 4.0