Pandas access first column with duplicate column names - pandas

Looking for some help accessing the first empty df column that is also a duplicate name, by name.
Consider this dataframe
import pandas as pd
df = pd.DataFrame(columns=['A', 'B', 'C', 'C', 'C', 'C', 'D', 'E'], index=[0,1,2,3])
A B C C C C D E
0 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN
then access a slice by indexer and column name
indexer = [1,3]
df.loc[indexer, 'C']
C C C C
1 NaN NaN NaN NaN
3 NaN NaN NaN NaN
I want to edit only the first instance of column C so that I get
A B C C C C D E
0 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN 99 NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN 99 NaN NaN NaN NaN NaN
I tried df.loc[indexer, 'C'].iloc[:,0] = 99
But it did not set the values.
Thanks in advance for your replies and ideas.

IIUC:
indexer = [1, 3]
col = (df.columns == 'C').argmax()
df.iloc[indexer, col] = 99
df
A B C C C C D E
0 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN 99 NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN 99 NaN NaN NaN NaN NaN

I would use index.get_loc to get the slice of integer location of columns C and passing its start to .iloc as follows:
indexer = [1, 3]
df.iloc[indexer, df.columns.get_loc('C').start] = 99
Or using np.nonzero
c_loc = np.nonzero(df.columns == 'C')[0]
df.iloc[indexer, c_loc[0]] = 99
Out[87]:
A B C C C C D E
0 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN 99 NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN 99 NaN NaN NaN NaN NaN

Related

Pandas DataFrame subtraction is getting an unexpected result. Concatenating instead?

I have two dataframes of the same size (510x6)
preds
0 1 2 3 4 5
0 2.610270 -4.083780 3.381037 4.174977 2.743785 -0.766932
1 0.049673 0.731330 1.656028 -0.427514 -0.803391 -0.656469
2 -3.579314 3.347611 2.891815 -1.772502 1.505312 -1.852362
3 -0.558046 -1.290783 2.351023 4.669028 3.096437 0.383327
4 -3.215028 0.616974 5.917364 5.275736 7.201042 -0.735897
... ... ... ... ... ... ...
505 -2.178958 3.918007 8.247562 -0.523363 2.936684 -3.153375
506 0.736896 -1.571704 0.831026 2.673974 2.259796 -0.815212
507 -2.687474 -1.268576 -0.603680 5.571290 -3.516223 0.752697
508 0.182165 0.904990 4.690155 6.320494 -2.326415 2.241589
509 -1.675801 -1.602143 7.066843 2.881135 -5.278826 1.831972
510 rows × 6 columns
outputStats
0 1 2 3 4 5
0 2.610270 -4.083780 3.381037 4.174977 2.743785 -0.766932
1 0.049673 0.731330 1.656028 -0.427514 -0.803391 -0.656469
2 -3.579314 3.347611 2.891815 -1.772502 1.505312 -1.852362
3 -0.558046 -1.290783 2.351023 4.669028 3.096437 0.383327
4 -3.215028 0.616974 5.917364 5.275736 7.201042 -0.735897
... ... ... ... ... ... ...
505 -2.178958 3.918007 8.247562 -0.523363 2.936684 -3.153375
506 0.736896 -1.571704 0.831026 2.673974 2.259796 -0.815212
507 -2.687474 -1.268576 -0.603680 5.571290 -3.516223 0.752697
508 0.182165 0.904990 4.690155 6.320494 -2.326415 2.241589
509 -1.675801 -1.602143 7.066843 2.881135 -5.278826 1.831972
510 rows × 6 columns
when I execute:
preds - outputStats
I expect a 510 x 6 dataframe with elementwise subtraction. Instead I get this:
0 1 2 3 4 5 0 1 2 3 4 5
0 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
2 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
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ...
505 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
506 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
507 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
508 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
509 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
I've tried dropping columns and the like, and that hasn't helped. I also get the same result with preds.subtract(outputStats). Any Ideas?
There are many ways that two different values can appear the same when displayed. One of the main ways is if they are different types, but corresponding values for those types. For instance, depending on how you're displaying them, the int 1 and the str '1' may not be easily distinguished. You can also have whitespace characters, such as '1' versus ' 1'.
If the problem is that one set is int while the other is str, you can solve the problem by converting them all to int or all to str. To do the former, do df.columns = [int(col) for col in df.columns]. To do the latter, df.columns = [str(col) for col in df.columns]. Converting to str is somewhat safer, as trying to convert to int can raise an error if the string isn't amenable to conversion (e.g. int('y') will raise an error), but int can be more usual as they have the numerical structure.
You asked in a comment about dropping columns. You can do this with drop and including axis=1 as a parameter to tell it to drop columns rather than rows, or you can use the del keyword. But changing the column names should remove the need to drop columns.

Filtering/Querying Pandas DataFrame after multiple grouping/agg

I have a dataframe that I first group, Counting QuoteLine Items grouped by stock(1-true, 0-false) and mfg type (K-Kit, M-manufactured, P-Purchased). Ultimately, I am interested in quotes that ALL items are either NonStock/Kit and/or Stock/['M','P'] :
grouped = df.groupby(['QuoteNum', 'typecode', 'stock']).agg({"QuoteLine": "count"})
and I get this:
QuoteLine-count
QuoteNum typecode stock
10001 K 0 1
10003 M 0 1
10005 M 0 3
1 1
10006 M 1 1
... ... ... ...
26961 P 1 1
26962 P 1 1
26963 P 1 2
26964 K 0 1
M 1 2
If I unstack it twice:
grouped = df.groupby(['QuoteNum', 'typecode', 'stock']).agg({"QuoteLine": "count"}).unstack().unstack()
# I get
QuoteLine-count
stock 0 1
typecode K M P K M P
QuoteNum
10001 1.0 NaN NaN NaN NaN NaN
10003 NaN 1.0 NaN NaN NaN NaN
10005 NaN 3.0 NaN NaN 1.0 NaN
10006 NaN NaN NaN NaN 1.0 NaN
10007 2.0 NaN NaN NaN NaN NaN
... ... ... ... ... ... ...
26959 NaN NaN NaN NaN NaN 1.0
26961 NaN 1.0 NaN NaN NaN 1.0
26962 NaN NaN NaN NaN NaN 1.0
26963 NaN NaN NaN NaN NaN 2.0
26964 1.0 NaN NaN NaN 2.0 NaN
Now I need to filter out all records where, this is where I need help
# pseudo-code
(stock == 0 and typecode in ['M','P']) -> values are NOT NaN (don't want those)
and
(stock == 1 and typecode='K') -> values are NOT NaN (don't want those either)
so I'm left with these records:
Basically: Columns "0/M, 0/P, 1/K" must be all NaNs and other columns have at least one non NaN value
QuoteLine-count
stock 0 1
typecode K M P K M P
QuoteNum
10001 1.0 NaN NaN NaN NaN NaN
10006 NaN NaN NaN NaN 1.0 NaN
10007 2.0 NaN NaN NaN NaN NaN
... ... ... ... ... ... ...
26959 NaN NaN NaN NaN NaN 1.0
26962 NaN NaN NaN NaN NaN 1.0
26963 NaN NaN NaN NaN NaN 2.0
26964 1.0 NaN NaN NaN 2.0 NaN
IIUC, use boolean mask to set rows that match your conditions to NaN then unstack desired levels:
# Shortcut (for readability)
lvl_vals = grouped.index.get_level_values
m1 = (lvl_vals('typecode') == 'K') & (lvl_vals('stock') == 0)
m2 = (lvl_vals('typecode').isin(['M', 'P'])) & (lvl_vals('stock') == 1)
grouped[m1|m2] = np.nan
out = grouped.unstack(level=['stock', 'typecode']) \
.loc[lambda x: x.isna().all(axis=1)]
Output result:
>>> out
QuoteLine-count
stock 0 1
typecode K M M P
QuoteNum
10001 NaN NaN NaN NaN
10006 NaN NaN NaN NaN
26961 NaN NaN NaN NaN
26962 NaN NaN NaN NaN
26963 NaN NaN NaN NaN
26964 NaN NaN NaN NaN
Desired values could be obtained by as_index==False, but i am not sure if they are in desired format.
grouped = df.groupby(['QuoteNum', 'typecode', 'stock'], as_index=False).agg({"QuoteLine": "count"})
grouped[((grouped["stock"]==0) & (grouped["typecode"].isin(["M" ,"P"]))) | ((grouped["stock"]==1) & (grouped["typecode"].isin(["K"])))]

pandas pivot_table aggfunc wrong value

enter image description here
df_6m_sum = df_6m.pivot_table(index='ACC_NBR', columns='class', values='TRANS_CHARGE', aggfunc=np.sum)
df_6m_sum.head(10)
class bus enter busi campus online offline drink buy change finance
ACC_NBR
1300xxx0265 NaN NaN NaN NaN NaN NaN NaN 11700.0 NaN NaN
1300xxx0272 NaN NaN NaN NaN NaN NaN NaN 13500.0 NaN NaN
1300xxx0659 NaN NaN NaN NaN NaN NaN NaN 152300.0 NaN NaN
1300xxx0928 NaN NaN NaN NaN NaN NaN NaN 140000.0 NaN NaN
1300xxx1117 900.0 NaN NaN NaN 5500.0 2870.0 NaN 18020.0 10003.0 NaN
1300xxx1170 NaN NaN NaN NaN NaN NaN NaN 143000.0 NaN NaN
1300xxx1683 NaN NaN NaN NaN NaN NaN NaN 5200.0 NaN NaN
1300xxx1916 NaN NaN NaN NaN NaN NaN NaN NaN 10983.0 NaN
1300xxx2212 NaN NaN NaN NaN NaN NaN NaN 207030.0 NaN NaN
1300xxx2939 1800.0 NaN 5000.0 NaN NaN NaN NaN NaN NaN NaN
df_6m_sum.iloc[9]
class
交通 1800.0
娱乐 NaN
政企 5000.0
校园 NaN
线上缴费 NaN
线上购物 NaN
线上餐饮 NaN
线下购物 NaN
账户变动 NaN
金融 NaN
Name: 1300xxx2939, dtype: float64
df_6m_sum.loc['1300xxx2939']
class
交通 1400.0
娱乐 NaN
政企 7000.0
校园 NaN
线上缴费 NaN
线上购物 NaN
线上餐饮 NaN
线下购物 NaN
账户变动 NaN
金融 NaN
Name: 1300xxx2939, dtype: float64
I use pandas pivot_table to sum money, but the result is wrong (right value is 1400, 7000), why i use different select method get different value ,please help me ,thanks you
Problem solved, 1300xxx2939 have two type of data : str and int, both in acc_nbr column, so have two different value

In Python, how can I update multiple rows in a DataFrame with a Series?

I have a dataframe as below.
a b c d
2010-07-23 NaN NaN NaN NaN
2010-07-26 NaN NaN NaN NaN
2010-07-27 NaN NaN NaN NaN
2010-07-28 NaN NaN NaN NaN
2010-07-29 NaN NaN NaN NaN
2010-07-30 NaN NaN NaN NaN
2010-08-02 NaN NaN NaN NaN
2010-08-03 NaN NaN NaN NaN
2010-08-04 NaN NaN NaN NaN
2010-08-05 NaN NaN NaN NaN
And I have a series as below.
2010-07-23
a 1
b 2
c 3
d 4
I want to update the DataFrame with the series as below. How can I do?
a b c d
2010-07-23 NaN NaN NaN NaN
2010-07-26 1 2 3 4
2010-07-27 1 2 3 4
2010-07-28 1 2 3 4
2010-07-29 NaN NaN NaN NaN
2010-07-30 NaN NaN NaN NaN
2010-08-02 NaN NaN NaN NaN
2010-08-03 NaN NaN NaN NaN
2010-08-04 NaN NaN NaN NaN
2010-08-05 NaN NaN NaN NaN
Thank you very much for the help in advance.
If there is one column instead Series in s add DataFrame.squeeze with concat by length of date range, last pass to DataFrame.update:
r = pd.date_range('2010-07-26','2010-07-28')
df.update(pd.concat([s.squeeze()] * len(r), axis=1, keys=r).T)
print (df)
a b c d
2010-07-23 NaN NaN NaN NaN
2010-07-26 1.0 2.0 3.0 4.0
2010-07-27 1.0 2.0 3.0 4.0
2010-07-28 1.0 2.0 3.0 4.0
2010-07-29 NaN NaN NaN NaN
2010-07-30 NaN NaN NaN NaN
2010-08-02 NaN NaN NaN NaN
2010-08-03 NaN NaN NaN NaN
2010-08-04 NaN NaN NaN NaN
2010-08-05 NaN NaN NaN NaN
Or you can use np.broadcast_to for repeat Series:
r = pd.date_range('2010-07-26','2010-07-28')
df1 = pd.DataFrame(np.broadcast_to(s.squeeze().values, (len(r),len(s))),
index=r,
columns=s.index)
print (df1)
a b c d
2010-07-26 1 2 3 4
2010-07-27 1 2 3 4
2010-07-28 1 2 3 4
df.update(df1)
print (df)
a b c d
2010-07-23 NaN NaN NaN NaN
2010-07-26 1.0 2.0 3.0 4.0
2010-07-27 1.0 2.0 3.0 4.0
2010-07-28 1.0 2.0 3.0 4.0
2010-07-29 NaN NaN NaN NaN
2010-07-30 NaN NaN NaN NaN
2010-08-02 NaN NaN NaN NaN
2010-08-03 NaN NaN NaN NaN
2010-08-04 NaN NaN NaN NaN
2010-08-05 NaN NaN NaN NaN

High Dimensional Data with time series

I am trying to make a pandas dataframe to hold my experimental data. The data is described below:
I have ~300 individuals participating in an experiment made of ~200 trials, where each trial has a number of experimentally controlled parameters (~10 parameters). For every trial and every individual I have a timeseries of some measurement, which is 30 timepoints long.
What is the best way to structure this data into a dataframe? I will need to be able to do things like get the experimental values for every individual at a certain time during all the trials with certain parameters, or get average values over certain times and trials for an indivudal, etc. Basically I will need to be able to slice this data in most conceivable ways.
Thanks!
EDIT: If you want to look at how I have my data at the moment, scroll down to the last 3 cells in this notebook: https://drive.google.com/file/d/1UZG_S2fg4MzaED8cLwE-nKHG0SHqevUr/view?usp=sharing
The data variable has all the parameters for each trial, and the interp_traces variable is an array of the timeseries measurements for each timepoint, individual, and trial.
I'd like to put everything in one thing if possible. The multi-index looks promising.
In my opinion need MultiIndex.
Sample:
individuals = list('ABCD')
trials = list('ab')
par = list('xyz')
dates = pd.date_range('2018-01-01', periods=5)
n = ['ind','trials','pars']
mux = pd.MultiIndex.from_product([individuals, trials, par], names=n)
df = pd.DataFrame(index=mux, columns=dates)
print (df)
2018-01-01 2018-01-02 2018-01-03 2018-01-04 2018-01-05
ind trials pars
A a x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN
b x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN
B a x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN
b x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN
C a x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN
b x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN
D a x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN
b x NaN NaN NaN NaN NaN
y NaN NaN NaN NaN NaN
z NaN NaN NaN NaN NaN