Range (list) as dummy columns - python

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)

Related

Max consecutive NaN's and filling them with values

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

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.

Add single field on df end

Is it possible after dataframe with 20+ rows and xx+ columns to add a single field with total count of certain value. User will add different values to df and before 'pandas.DataFrame.to_excel' it's neccesary to to add a single field with some specific data. Like in the attached picture. Is it possible to add a single field after an already structured df?
This can work for you:
Df:
A B output
0 a 1.0 1.0
1 a 2.0 1.0
2 a 3.0 1.0
3 a 4.0 1.0
4 a 5.0 1.0
for i in range(df.iloc[-1].name + 1, 25): # Add 20 new nan row (you can change it)
df.loc[i, :] = np.nan
df.loc[df.iloc[-1].name + 1, 'A'] = 'Result: ' + str(df['B'].sum()) # For this example i just put sum of column B so you can change it.
print(df)
A B output
0 a 1.0 1.0
1 a 2.0 1.0
2 a 3.0 1.0
3 a 4.0 1.0
4 a 5.0 1.0
5 NaN NaN NaN
6 NaN NaN NaN
7 NaN NaN NaN
8 NaN NaN NaN
9 NaN NaN NaN
10 NaN NaN NaN
11 NaN NaN NaN
12 NaN NaN NaN
13 NaN NaN NaN
14 NaN NaN NaN
15 NaN NaN NaN
16 NaN NaN NaN
17 NaN NaN NaN
18 NaN NaN NaN
19 NaN NaN NaN
20 NaN NaN NaN
21 NaN NaN NaN
22 NaN NaN NaN
23 NaN NaN NaN
24 NaN NaN NaN
25 Result: 15.0 NaN NaN

Divide Dataframe by a series sharing index

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

Python Pandas Pivot Table

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']

Categories

Resources