Set Column Value Based on Calculate Condition from Each Row - python

I have a empty dataframe as
columns_name = list(str(i) for i in range(10))
dfa = pd.DataFrame(columns=columns_name, index=['A', 'B', 'C', 'D'])
dfa['Count'] = [10, 6, 9, 4]
0
1
2
3
4
5
6
7
8
9
Count
A
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
10
B
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
6
C
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
9
D
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
4
I want to replace Nan values with a symbol with the difference of max(Count) - Current(max).
So, the final result will look like.
0
1
2
3
4
5
6
7
8
9
Count
A
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
10
B
NaN
NaN
NaN
NaN
NaN
NaN
-
-
-
-
6
C
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
-
9
D
NaN
NaN
NaN
NaN
-
-
-
-
-
-
4
I am stuck at
dfa.at[dfa.index, [str(col) for col in list(range(dfa['Count'].max() - dfa['Count']))]] = '-'
and getting KeyError: 'Count'

Actually, your this part of the code dfa.at[dfa.index, [str(col) for col in list(range(dfa['Count'].max() - dfa['Count']))]] = '-' has issue.
Just try to create the list which you are trying to use inside comprehension
list(range(dfa['Count'].max() - dfa['Count']))
It'll throw TypeError
If you notice, you'll figure out that (dfa['Count'].max() - dfa['Count']) will give following series:
A 0
B 4
C 1
D 6
And since you're trying to pass a series to python's range function, it will throw the error.
One possible solution might be:
for index, cols in zip(dfa.index, [list(map(str, col)) for col in (dfa).apply(lambda x: list(range(x['Count'], dfa['Count'].max())), axis=1).values]):
dfa.loc[index, cols] = '-'
OUTPUT:
Out[315]:
0 1 2 3 4 5 6 7 8 9 Count
A NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 10
B NaN NaN NaN NaN NaN NaN - - - - 6
C NaN NaN NaN NaN NaN NaN NaN NaN NaN - 9
D NaN NaN NaN NaN - - - - - - 4

Broadcasting is also an option:
import pandas as pd
import numpy as np
columns_name = list(str(i) for i in range(10))
dfa = pd.DataFrame(columns=columns_name, index=['A', 'B', 'C', 'D'])
dfa['Count'] = [10, 6, 9, 4]
# Broadcast based on column index (Excluding Count)
m = (
dfa['Count'].to_numpy()[:, None] == np.arange(0, dfa.shape[1] - 1)
).cumsum(axis=1).astype(bool)
# Grab Columns To Update
non_count_columns = dfa.columns[dfa.columns != 'Count']
# Update based on mask
dfa[non_count_columns] = dfa[non_count_columns].mask(m, '-')
print(dfa)
Output:
0 1 2 3 4 5 6 7 8 9 Count
A NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 10
B NaN NaN NaN NaN NaN NaN - - - - 6
C NaN NaN NaN NaN NaN NaN NaN NaN NaN - 9
D NaN NaN NaN NaN - - - - - - 4

Related

How do you shift each row in pandas data frame by a specific value?

If I have a pandas dataframe like this:
2 3 4 NaN NaN NaN
1 NaN NaN NaN NaN NaN
5 6 7 2 3 NaN
4 3 NaN NaN NaN NaN
and an array for the number I would like to shift:
array = [2, 4, 0, 3]
How do I iterate through each row to shift the columns by the number in my array to get something like this:
NaN NaN 2 3 4 NaN
NaN NaN NaN NaN 1 NaN
5 6 7 2 3 NaN
NaN NaN NaN 3 4 NaN
I was trying to do something like this but had no luck.
df = pd.DataFrame(values)
for rows in df.iterrows():
df[rows] = df.shift[change_in_bins[rows]]
Use for loop with loc and shift:
for index,value in enumerate([2, 4, 0, 3]):
df.loc[index,:] = df.loc[index,:].shift(value)
print(df)
0 1 2 3 4 5
0 NaN NaN 2.0 3.0 4.0 NaN
1 NaN NaN NaN NaN 1.0 NaN
2 5.0 6.0 7.0 2.0 3.0 NaN
3 NaN NaN NaN 4.0 3.0 NaN

How to drop column according to NAN percentage for dataframe?

For certain columns of df, if 80% of the column is NAN.
What's the simplest code to drop such columns?
You can use isnull with mean for threshold and then remove columns by boolean indexing with loc (because remove columns), also need invert condition - so <.8 means remove all columns >=0.8:
df = df.loc[:, df.isnull().mean() < .8]
Sample:
np.random.seed(100)
df = pd.DataFrame(np.random.random((100,5)), columns=list('ABCDE'))
df.loc[:80, 'A'] = np.nan
df.loc[:5, 'C'] = np.nan
df.loc[20:, 'D'] = np.nan
print (df.isnull().mean())
A 0.81
B 0.00
C 0.06
D 0.80
E 0.00
dtype: float64
df = df.loc[:, df.isnull().mean() < .8]
print (df.head())
B C E
0 0.278369 NaN 0.004719
1 0.670749 NaN 0.575093
2 0.209202 NaN 0.219697
3 0.811683 NaN 0.274074
4 0.940030 NaN 0.175410
If want remove columns by minimal values dropna working nice with parameter thresh and axis=1 for remove columns:
np.random.seed(1997)
df = pd.DataFrame(np.random.choice([np.nan,1], p=(0.8,0.2),size=(10,10)))
print (df)
0 1 2 3 4 5 6 7 8 9
0 NaN NaN NaN 1.0 1.0 NaN NaN NaN NaN NaN
1 1.0 NaN 1.0 NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN 1.0 1.0 NaN NaN NaN
3 NaN NaN NaN NaN 1.0 NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN 1.0 NaN NaN NaN 1.0
5 NaN NaN NaN 1.0 1.0 NaN NaN 1.0 NaN 1.0
6 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN 1.0 NaN NaN
9 1.0 NaN NaN NaN 1.0 NaN NaN 1.0 NaN NaN
df1 = df.dropna(thresh=2, axis=1)
print (df1)
0 3 4 5 7 9
0 NaN 1.0 1.0 NaN NaN NaN
1 1.0 NaN NaN NaN NaN NaN
2 NaN NaN NaN 1.0 NaN NaN
3 NaN NaN 1.0 NaN NaN NaN
4 NaN NaN NaN 1.0 NaN 1.0
5 NaN 1.0 1.0 NaN 1.0 1.0
6 NaN NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN
8 NaN NaN NaN NaN 1.0 NaN
9 1.0 NaN 1.0 NaN 1.0 NaN
EDIT: For non-Boolean data
Total number of NaN entries in a column must be less than 80% of total entries:
df = df.loc[:, df.isnull().sum() < 0.8*df.shape[0]]
df.dropna(thresh=np.int((100-percent_NA_cols_required)*(len(df.columns)/100)),inplace=True)
Basically pd.dropna takes number(int) of non_na cols required if that row is to be removed.
You can use the pandas dropna. For example:
df.dropna(axis=1, thresh = int(0.2*df.shape[0]), inplace=True)
Notice that we used 0.2 which is 1-0.8 since the thresh refers to the number of non-NA values
As suggested in comments, if you use sum() on a boolean test, you can get the number of occurences.
Code:
def get_nan_cols(df, nan_percent=0.8):
threshold = len(df.index) * nan_percent
return [c for c in df.columns if sum(df[c].isnull()) >= threshold]
Used as:
del df[get_nan_cols(df, 0.8)]

python, pandas - dataframe with time, create shifted data

I have a DataFrame:
df = pd.DataFrame(
np.random.rand(10, 3),
columns='sensor_id|unix_timestamp|value'.split('|'))
I want to create 5 more columns in which each new column is a shifted version of the value column.
sensor_id unix_timestamp value value_shift_0 value_shift_1 value_shift_2 value_shift_3 value_shift_4
0 0.901001 0.036683 0.945908 NaN NaN NaN NaN NaN
1 0.751759 0.038600 0.117308 NaN NaN NaN NaN NaN
2 0.737604 0.484417 0.602733 NaN NaN NaN NaN NaN
3 0.259865 0.522115 0.074188 NaN NaN NaN NaN NaN
4 0.932359 0.662560 0.648445 NaN NaN NaN NaN NaN
5 0.114668 0.066766 0.285553 NaN NaN NaN NaN NaN
6 0.795851 0.565259 0.888404 NaN NaN NaN NaN NaN
7 0.082534 0.355506 0.671816 NaN NaN NaN NaN NaN
8 0.336648 0.651789 0.859373 NaN NaN NaN NaN NaN
9 0.917073 0.842281 0.458542 NaN NaN NaN NaN NaN
But I don't know how to fill in with the appropriated shifted value columns.
pd.concat with a dictionary comprehension along with join
df.join(
pd.concat(
{'value_shift_{}'.format(i): df.value.shift(i) for i in range(5)},
axis=1))
alternative with numpy
def multi_shift(s, n):
a = np.arange(len(s))
i = (a[:, None] - a[:n]).ravel()
e = np.empty(i.shape)
e.fill(np.nan)
w = np.where(i >= 0)
e[w] = df.value.values[i[w]]
return pd.DataFrame(e.reshape(10, -1),
s.index, ['shift_%i' % s for s in range(n)])
df.join(multi_shift(df.value, n))
timing

Broadcasting Error Pandas

I have a dataframe with 4 columns. I want to do an element-wise division of the first 3 columns by the value in 4th column
I tried:
df2 = pd.DataFrame(df.ix[:,['col1', 'col2', 'col3']].values / df.col4.values)
And I got this error:
ValueError: operands could not be broadcast together with shapes (19,3) (19,)
My solution was:
df2 = pd.DataFrame(df.ix[:,['col1', 'col2', 'col3']].values / df.col4.values.reshape(19,1))
This worked as I wanted, but to be robust for different numbers of rows I would need to do:
.reshape(len(df),1)
It just seems an ugly way to have to do something - is there a better way around the array shape being (19,) it seems odd that it has no second dimension.
Best Regards,
Ben
You can just do div and pass axis=0 to force the division to be performed column-wise:
df2 = pd.DataFrame(df.ix[:,['col1', 'col2', 'col3']].div(df.col4, axis=0))
Your error is because the division using / is being performed on the minor axis which in this case is the row axis and there is no direct alignment, see this example:
In [220]:
df = pd.DataFrame(columns=list('abcd'), data = np.random.randn(8,4))
df
Out[220]:
a b c d
0 1.074803 0.173520 0.211027 1.357138
1 1.418757 -1.879024 0.536826 1.006160
2 -0.029716 -1.146178 0.100900 -1.035018
3 0.314665 -0.773723 -1.170653 0.648740
4 -0.179666 1.291836 -0.009614 0.392149
5 0.264599 -0.057409 -1.425638 1.024098
6 -0.106062 1.824375 0.595974 1.167115
7 0.601544 -1.237881 0.106854 -1.276829
In [221]:
df.ix[:,['a', 'b', 'c']]/df['d']
Out[221]:
a b c 0 1 2 3 4 5 6 7
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
6 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
This isn't obvious until you understand how broadcasting works.

pandas - checking a condition for each group in a dataframe

I have got a dataframe:
df = pd.DataFrame({'index' : range(8),
'variable1' : ["A","A","B","B","A","B","B","A"],
'variable2' : ["a","b","a","b","a","b","a","b"],
'variable3' : ["x","x","x","y","y","y","x","y"],
'result': [1,0,0,1,1,0,0,1]})
df2 = df.pivot_table(values='result',rows='index',cols=['variable1','variable2','variable3'])
df2['A']['a']['x'][4] = 1
df2['B']['a']['x'][3] = 1
variable1 A B
variable2 a b a b
variable3 x y x y x y
index
0 1 NaN NaN NaN NaN NaN
1 NaN NaN 0 NaN NaN NaN
2 NaN NaN NaN NaN 0 NaN
3 NaN NaN NaN NaN 1 1
4 1 1 NaN NaN NaN NaN
5 NaN NaN NaN NaN NaN 0
6 NaN NaN NaN NaN 0 NaN
7 NaN NaN NaN 1 NaN NaN
Now I want to check for simultaneous occurrences of x == 1 and y == 1, but only within each subgroup, defined by variable1 and variable2. So, for the dataframe shown above, the condition is met for index == 4 (group A-a), but not for index == 3 (groups B-a and B-b).
I suppose some groupby() magic would be needed, but I cannot find the right way. I have also tried experimenting with a stacked dataframe (using df.stack()), but this did not get me any closer...
You can use groupby on the 2 first levels variable1 and variable2 to get the sum of the x and y columns at that level:
r = df2.groupby(level=[0,1], axis=1).sum()
r
Out[50]:
variable1 A B
variable2 a b a b
index
0 1 NaN NaN NaN
1 NaN 0 NaN NaN
2 NaN NaN 0 NaN
3 NaN NaN 1 1
4 2 NaN NaN NaN
5 NaN NaN NaN 0
6 NaN NaN 0 NaN
7 NaN 1 NaN NaN
Consequently, the lines you are searching for are the ones that contain the value 2:
r[r==2].dropna(how='all')
Out[53]:
variable1 A B
variable2 a b a b
index
4 2 NaN NaN NaN

Categories

Resources