我确实有一个包含两列的数据框:date
和bill_id
。日期列中的日期范围是从01-01-2017到30-12-2017的一年。有1000个唯一的bill_ids
。每个bill_id
在bill_id
列中可能至少出现一次。结果是一个DataFrame,大小为:2列,1000000行...
dt |bill_id
01-01-2017 bill_1
01-01-2017 bill_2
02-01-2017 bill_1
02-01-2017 bill_3
03-01-2017 bill_4
03-01-2017 bill_4
因此,某些name_id可能在特定日期出现,而其他的不是。
我要实现的是一种格式的数据帧,因此所有唯一的bill_ids是列,所有唯一的日期是行,并且每个bill_id对应于对应的日期值具有0或1或2,其中0 =尚未在该日期出现,其中1个出现在该日期,2个没有出现在该日期,但是在例如
如果bill_id在2017年2月1日存在,则它将在2017年1月1日为0,在2017年1月1日为1,在2017年3月1日为2,在所有连续的日子中为2。
我做了几个步骤,但是由于速度很慢,代码无法扩展:
def map_values(row, df_z, c):
subs = df_z[[c, 'bill_id', 'date']].loc[df_z['date'] == row['dt']]
if c not in subs['bill_id']:
row[c] = max(subs[c].tolist())
else:
val = df_z[c].loc[(df_z['date'] == row['dt']) & (df_z['bill_id'] == c)].values
assert len(val) == 1
row[c] = val[0]
return row
def map_to_one(x):
bills_x = x['bill_id'].tolist()
for b in bills_x:
try:
x[b].loc[x['bill_id'] == b] = 1
except:
pass
return x
def replace_val(df_groupped, col):
mask = df_groupped.loc[df_groupped['bill_id'] == col].index[df_groupped[col].loc[df_groupped['bill_id'] == col] == 1]
min_dt = df_groupped.iloc[min(mask)]['date']
max_dt = df_groupped.iloc[max(mask)]['date']
df_groupped[col].loc[(df_groupped['date'] < min_dt)] = 0
df_groupped[col].loc[(df_groupped['date'] >= min_dt) & (df_groupped['date'] <= max_dt)] = 1
df_groupped[col].loc[(df_groupped['date'] > max_dt)] = 2
return df_groupped
def reduce_cols(row):
col_id = row['bill_id']
row['val'] = row[col_id]
return row
df = df.sort_values(by='date')
df = df[pd.notnull(df['bill_id'])]
bills = list(set(df['bill_id'].tolist()))
for col in bills:
df[col] = 9
df_groupped = df.groupby('date')
df_groupped = df_groupped.apply(lambda x: map_to_one(x))
df_groupped = df_groupped.reset_index()
df_groupped.to_csv('groupped_in.csv', index=False)
df_groupped = pd.read_csv('groupped_in.csv')
for col in bills:
df_groupped = replace_val(df_groupped, col)
df_groupped = df_groupped.apply(lambda row: reduce_cols(row), axis=1)
df_groupped.to_csv('out.csv', index=False)
cols = [x for x in df_groupped.columns if x not in ['index', 'date', 'bill_id', 'val']]
col_dt = sorted(list(set(df_groupped['date'].tolist())))
dd = {x:[0]*len(col_dt) for x in cols}
dd['dt'] = col_dt
df_mapped = pd.DataFrame(data=dd).set_index('dt').reset_index()
for c in cols:
counter += 1
df_mapped = df_mapped.apply(lambda row: map_values(row, df_groupped[[c, 'bill_id', 'date']], c), axis=1)
编辑:
乔的回答很好,但我决定改用其他选择:
答案 0 :(得分:1)
我希望我能理解您想要的输出。
首先创建一个crosstab
:
df1 = pd.crosstab(df['dt'],df['bill_id'])
输出:
bill_id bill_1 bill_2 bill_3 bill_4
dt
01-01-2017 1 1 0 0
02-01-2017 1 0 1 0
03-01-2017 0 0 0 2
从现在开始,您将以这种方式修改df: 创建将用作遮罩的副本
df2 = df1.copy()
将0
替换为1(或其他值> 1)之后:
for col in df2.columns:
df2[col] = df2[col].replace(to_replace=0, method='ffill')
bill_id bill_1 bill_2 bill_3 bill_4
dt
01-01-2017 1 1 0 0
02-01-2017 1 1 1 0
03-01-2017 1 1 1 2
现在减去2 df:
df3 = df1-df2
这些是更改的值:
bill_id bill_1 bill_2 bill_3 bill_4
dt
01-01-2017 0 0 0 0
02-01-2017 0 -1 0 0
03-01-2017 -1 -1 -1 0
将这些值替换为2:
for col in df3.columns:
df3[col] = df3[col].replace(-1, 2)
返回第一个df1并将大于1的值更改为1:
for col in df1.columns:
df1[col] = df1[col].apply(lambda x: x if x < 2 else 1)
最后,您将最后一个df与df3相加:
df_add = df1.add(df3, fill_value=0)
输出:
bill_id bill_1 bill_2 bill_3 bill_4
dt
01-01-2017 1 1 0 0
02-01-2017 1 2 1 0
03-01-2017 2 2 2 1
要完成操作,请替换负值:
for col in df_add.columns:
df_add[col] = df_add[col].apply(lambda x: 2 if x < 0 else x)