I want to get the number of consecutive NaN's in each column and if the maximum of these consecutive NaN's are smaller than, let's say 3, then I want to fill those with the first prior non-NaN value, and if it's more than 3, then remove the whole column. Here's a small part of my dataset to work with.
>>> df
113550 100285 112283 101668 114157 100019
0 NaN 27.60000 NaN NaN NaN NaN
1 NaN 27.50000 NaN NaN 36.25000 NaN
2 NaN 27.25000 NaN NaN 36.25000 22.5
3 NaN 27.90000 NaN NaN 47.33333 22.5
4 NaN 28.00000 NaN NaN NaN NaN
5 NaN 27.66667 NaN NaN 36.25000 NaN
6 NaN 26.41667 NaN NaN 40.00000 NaN
7 NaN NaN NaN NaN 36.25000 NaN
8 NaN 27.87500 NaN NaN 41.87500 22.5
9 NaN 27.85000 NaN NaN 46.66667 22.5
10 NaN 27.45000 NaN NaN 40.00000 22.5
11 NaN 27.45000 NaN NaN 41.75000 NaN
12 NaN 26.43750 NaN NaN 40.00000 NaN
13 NaN 26.50000 NaN NaN 41.75000 NaN
14 NaN 26.60000 NaN NaN 41.75000 22.5
15 NaN 26.60000 NaN NaN 41.75000 22.5
16 NaN 24.62500 NaN NaN 39.83333 NaN
17 NaN 24.60000 NaN NaN 41.75000 NaN
18 NaN 24.50000 NaN NaN NaN 22.5
19 NaN 23.62500 NaN NaN 41.87500 NaN
From Identifying consecutive NaNs with Pandas, you can use:
consecutive_nans = lambda x: x.isna().groupby(x.notna().cumsum()).sum().max()
out = df[df.apply(consecutive_nans).loc[lambda x: x <= 3].index].ffill().bfill()
print(out)
# Output
100285 114157
0 27.60000 36.25000
1 27.50000 36.25000
2 27.25000 36.25000
3 27.90000 47.33333
4 28.00000 47.33333
5 27.66667 36.25000
6 26.41667 40.00000
7 26.41667 36.25000
8 27.87500 41.87500
9 27.85000 46.66667
10 27.45000 40.00000
11 27.45000 41.75000
12 26.43750 40.00000
13 26.50000 41.75000
14 26.60000 41.75000
15 26.60000 41.75000
16 24.62500 39.83333
17 24.60000 41.75000
18 24.50000 41.75000
19 23.62500 41.87500
Related
I have the following PDF file from which I want to get the the data inside it so as i can integrate with my app.
Example i want to get 1 for Monday and 10 and 14 for the columns having white boxes
Here is what I have tried:
import tabula
df = tabula.read_pdf("IT.pdf",multiple_tables=True)
for col in df:
print(col)
The output comes like
07:00 08:00 08:00 09:00 Unnamed: 0 Unnamed: 1 ... Unnamed: 10 07:00 08:00.1 Unnamed: 11 08:00 09:00.1
0 Tutorial Tutorial NaN NaN ... NaN Tutorial NaN NaN
1 G1_MSU G1G2G3_M NaN NaN ... NaN SPU_07410 NaN NaN
2 07201 TU 07203 NaN NaN ... NaN 110 NaN NaN
3 110 110, 115, NaN NaN ... NaN Andaray, N NaN NaN
4 Lema, F (Mr) 117 NaN NaN ... NaN (Mr) NaN NaN
5 BscIRM__1 Farha, M NaN NaN ... NaN BIRM__2PT NaN NaN
6 C (Mrs), NaN NaN ... NaN NaN NaN NaN
7 NaN Mandia, A NaN NaN ... NaN NaN NaN NaN
8 NaN (Ms), NaN NaN ... NaN NaN NaN NaN
9 NaN Wilberth, N NaN NaN ... NaN NaN NaN NaN
10 NaN (Ms) NaN NaN ... NaN NaN NaN NaN
11 NaN BscIRM__1 NaN NaN ... NaN NaN NaN NaN
12 NaN C NaN NaN ... NaN NaN NaN NaN
13 Tutorial Tutorial NaN NaN ... NaN Tutorial NaN Tutorial
14 G4_MSU G3_MTU NaN NaN ... NaN AFT_05204 NaN BFT_05202
15 07201 07203 NaN NaN ... NaN 110 NaN 110
use camelot package. That will help you.
I have two columns with start and end range. I want make dummy columns for range between this columns. I can make it by apply method, but it is very slow. Can I make it without apply (because I have ~2-5M rows).
Entire DataFrame:
start end
0 36 36
1 31 31
2 29 29
3 10 10
4 35 35
5 42 44
6 24 26
What I want to see:
start end 8 9 10 24 25 26 29 31 35 36 42 43 44
0 36 36 NaN NaN NaN NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN
1 31 31 NaN NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN
2 29 29 NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN NaN
3 10 10 NaN NaN 1.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 35 35 NaN NaN NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN NaN
5 42 44 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 1.0 1.0 1.0
6 24 26 NaN NaN NaN 1.0 1.0 1.0 NaN NaN NaN NaN NaN NaN NaN
7 25 25 NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN NaN NaN NaN
8 35 35 NaN NaN NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN NaN
9 8 10 1.0 1.0 1.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
Now I use this code:
import itertools
def zip_with_scalar(l, o):
return dict(zip(l, itertools.repeat(o)))
df.merge(df.apply(lambda s: pd.Series(zip_with_scalar(range(s['start'], s['end']+1), 1)), axis = 1), left_index=True, right_index=True)
Use list comprehension with DataFrame constructor:
a = [dict.fromkeys(range(x, y), 1) for x, y in zip(df['start'], df['end']+1)]
df = df.join(pd.DataFrame(a, index=df.index))
print (df)
start end 10 24 25 26 29 31 35 36 42 43 44
0 36 36 NaN NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN
1 31 31 NaN NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN
2 29 29 NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN NaN
3 10 10 1.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 35 35 NaN NaN NaN NaN NaN NaN 1.0 NaN NaN NaN NaN
5 42 44 NaN NaN NaN NaN NaN NaN NaN NaN 1.0 1.0 1.0
6 24 26 NaN 1.0 1.0 1.0 NaN NaN NaN NaN NaN NaN NaN
Performance:
#[70000 rows x 2 columns]
df = pd.concat([df] * 10000, ignore_index=True)
def a(df):
a = [dict.fromkeys(range(x, y), 1) for x, y in zip(df['start'], df['end']+1)]
return df.join(pd.DataFrame(a, index=df.index))
import itertools
def zip_with_scalar(l, o):
return dict(zip(l, itertools.repeat(o)))
def b(df):
return df.merge(df.apply(lambda s: pd.Series(zip_with_scalar(range(s['start'], s['end']+1), 1)), axis = 1), left_index=True, right_index=True)
In [176]: %timeit a(df.copy())
202 ms ± 6.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [177]: %timeit b(df.copy())
38.9 s ± 1.19 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
I want to divide a DataFrame by one of its columns (a Series), they both share the index, so I expect the result has the shape of the original DataFrame.
This code shows what I did:
import numpy as np
import pandas as pd
cols = ['A', 'B', 'C', 'D']
ix = range(10)
df = pd.DataFrame(index=ix, columns=cols, data=np.random.randint(0, 100, size=(10, 4)))
print(df / df['A'])
The result is something like that:
0 1 2 3 4 5 6 7 8 9 A B C D
0 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
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
9 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
But I expect something like that:
A B C D
0 1 .. .. ..
1 1 .. .. ..
2 1 .. .. ..
3 1 .. .. ..
4 1 .. .. ..
5 1 .. .. ..
6 1 .. .. ..
7 1 .. .. ..
8 1 .. .. ..
9 89 94 14 44
Thanks in advance.
use div with axis=0, it's aligning on the columns hence you get the 0...9 and original columns, you should use div and explicitly pass axis=0 so it broadcasts along the index:
In [58]:
, axis=0
df.div(df['A'], axis=0)
Out[58]:
A B C D
0 1.0 0.818182 1.681818 0.431818
1 1.0 1.562500 0.625000 1.468750
2 1.0 17.000000 5.400000 2.800000
3 1.0 9.428571 13.857143 8.285714
4 1.0 0.256098 0.085366 1.146341
5 1.0 27.000000 21.500000 7.500000
6 1.0 0.444444 1.236111 1.041667
7 1.0 0.268293 0.048780 1.146341
8 1.0 0.505051 0.434343 0.101010
9 1.0 0.673684 0.378947 0.873684
You can see a related question: What does the term "broadcasting" mean in Pandas documentation? that illustrates the broadcasting rules
I have got the following dataframe, in which each column contains a set of values, and each index is only used once. However, I would like to get a completely filled dataframe. In order to do that I need to select, from each column, an X amount of values, in which X is the length of the column with the least non-nan values (in this case column '1.0').
>>> stat_df_iws
iws_w -2.0 -1.0 0.0 1.0
0 0.363567 NaN NaN NaN
1 0.183698 NaN NaN NaN
2 NaN -0.337931 NaN NaN
3 -0.231770 NaN NaN NaN
4 NaN 0.544836 NaN NaN
5 NaN -0.377620 NaN NaN
6 NaN NaN -0.428396 NaN
7 NaN NaN -0.443317 NaN
8 NaN -0.268033 NaN NaN
9 NaN 0.246714 NaN NaN
10 NaN NaN -0.503887 NaN
11 NaN NaN NaN -0.298935
12 NaN -0.252775 NaN NaN
13 NaN -0.447757 NaN NaN
14 -0.650598 NaN NaN NaN
15 -0.660542 NaN NaN NaN
16 NaN -0.952041 NaN NaN
17 -0.667356 NaN NaN NaN
18 -0.920873 NaN NaN NaN
19 NaN -0.537657 NaN NaN
20 NaN NaN -0.525121 NaN
21 NaN NaN NaN -0.619755
22 NaN -0.652138 NaN NaN
23 NaN -0.924181 NaN NaN
24 NaN -0.665720 NaN NaN
25 NaN NaN -0.336841 NaN
26 -0.428931 NaN NaN NaN
27 NaN -0.348248 NaN NaN
28 NaN 0.781024 NaN NaN
29 0.110727 NaN NaN NaN
... ... ... ... ...
I've achieved this with the following code, but it is not a very pythonic way of solving this.
def get_non_null_from_pivot(df):
lngth = min(list(len(col.dropna()) for ind, col in df.iteritems()))
df = pd.concat([df.loc[:,-2.0].dropna().head(lngth).reset_index(drop=True),\
df.loc[:,-1.0].dropna().head(lngth).reset_index(drop=True),\
df.loc[:,0.0].dropna().head(lngth).reset_index(drop=True),\
df.loc[:,1.0].dropna().head(lngth).reset_index(drop=True)], \
axis=1)
Is there a simpler way to achieve the same goal, so that I can more automatically repeat this step for other dataframes? Preferably without for-loops, for efficiency reasons.
I've made the function a little shorter by looping through the columns, and it seems to work perfectly.
def get_non_null_from_pivot_short(df):
lngth = min(list(len(col.dropna()) for ind, col in df.iteritems()))
df = pd.concat(list(df.loc[:,col].dropna().head(lngth).reset_index(drop=True) for col in df), \
axis=1)
return df
I am trying to do a pivot table of frequency counts using Pandas.
I have the following code:
from pandas import pivot_table, DataFrame, crosstab
import numpy as np
df=DataFrame(
{'Y':[99999991, 99999992, 99999993, 99999994, 99999995,
99999996, 99999997, 99999998, 99999999],
'X':[1, 2, 3, 4, 5, 6, 7, 8, 9],
'X2':[1, 2, 3, 4, 5, 6, 7, 8, 9]})
print pivot_table(df,rows=['Y'], cols=['X'],aggfunc=np.sum)
This is my output:
X 1 2 3 4 5 6 7 8 9
Y
99999991 1 NaN NaN NaN NaN NaN NaN NaN NaN
99999992 NaN 2 NaN NaN NaN NaN NaN NaN NaN
99999993 NaN NaN 3 NaN NaN NaN NaN NaN NaN
99999994 NaN NaN NaN 4 NaN NaN NaN NaN NaN
99999995 NaN NaN NaN NaN 5 NaN NaN NaN NaN
99999996 NaN NaN NaN NaN NaN 6 NaN NaN NaN
99999997 NaN NaN NaN NaN NaN NaN 7 NaN NaN
99999998 NaN NaN NaN NaN NaN NaN NaN 8 NaN
99999999 NaN NaN NaN NaN NaN NaN NaN NaN 9
This is my desired output:
X 1 2 3 4 5 6 7 8 9
X2
1 99999991 NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN 99999992 NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN 99999993 NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN 99999994 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN 99999995 NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN 99999996 NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN 99999997 NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN 99999998 NaN
9 NaN NaN NaN NaN NaN NaN NaN NaN 99999999
This is what I keep getting:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 9 entries, 1 to 9
Data columns:
('Y', 1L) 1 non-null values
('Y', 2L) 1 non-null values
('Y', 3L) 1 non-null values
('Y', 4L) 1 non-null values
('Y', 5L) 1 non-null values
('Y', 6L) 1 non-null values
('Y', 7L) 1 non-null values
('Y', 8L) 1 non-null values
('Y', 9L) 1 non-null values
dtypes: float64(9)
Does anyone know why? Is the output too big. I can't seem to find anything on it.
Just replace rows=['Y'] with rows=['X2']
>>> print pivot_table(df,rows=['X2'], cols=['X'],aggfunc=np.sum)
Y
X 1 2 3 4 5 6 7 8 9
X2
1 101 NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN 102 NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN 103 NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN 104 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN 105 NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN 106 NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN 107 NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN 108 NaN
9 NaN NaN NaN NaN NaN NaN NaN NaN 109
Try this:
In [3]: df.pivot_table('Y', rows='X', cols='X2')
X2 1 2 3 4 5 6 7 8 9
X
1 99999991 NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN 99999992 NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN 99999993 NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN 99999994 NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN 99999995 NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN 99999996 NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN 99999997 NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN 99999998 NaN
9 NaN NaN NaN NaN NaN NaN NaN NaN 99999999
This would also work:
pivot_table(df, 'Y', rows='X', cols='X2')
or
pivot_table(df, rows='X', cols='X2')['Y']