我有一个数据框:
ID URINE_TEST UNIT VALUE
1 'alb' mg 1500
2 'alb' mg 1200
3 'alb' mg 1600
4 'alb' g 1.2
5 'alb' g 1.8
7 'alb' NaN 1300 <- should become mg
8 'crt' l 2.3
9 'crt' l 3.3
10 'crt' l 4.1
11 'crt' ml 2500
12 'crt' ml 3400
13 'crt' ml 2100
14 'crt' NaN 3.0 <-should become l
15 'crt' NaN 99 <-should stay as NaN (not inside any range)
我想完成UNIT列为NaN的地方。
我在这里说明。让我们以尿液测试“ alb”(白蛋白)为例。如您所见,有一个测试结果(ID:7)没有指定单位。但是,仅通过查看其他单位(mg和g)的VALUE范围,就可以看出该NaN应该为mg。因为其值1300的值类似于mg的值(请参见表)。即它在[mg]值[1200-1600]的值范围内。
与尿液“ crt”(肌酐)(ID:14)相同,其中NaN单位应为l(升),只需查看单位l的值范围即可:[2.3-4.1]。最后,ID:15应该保留为NaN(不属于任何值范围)。
话虽如此,我想编写一个程序,如果该值与其他单位在同一范围内,则为大数据集中的所有尿液测试分配相应的单位。否则,将单位保留为NaN。
我已经开始做df.groupby([urine_test, unit]).value.transform('min')
和'max':
ID URINE_TEST UNIT VALUE MIN MAX
1 'alb' mg 1500 1200 1600
2 'alb' mg 1200 1200 1600
3 'alb' mg 1600 1200 1600
4 'alb' g 1.2 1.2 1.8
5 'alb' g 1.8 1.2 1.8
7 'alb' NaN 1300
8 'crt' l 2.3 2.3 4.1
9 'crt' l 3.3 2.3 4.1
10 'crt' l 4.1 2.3 4.1
11 'crt' ml 2500 2100 3400
12 'crt' ml 3400 2100 3400
13 'crt' ml 2100 2100 3400
14 'crt' NaN 3.0
15 'crt' NaN 99
但是我无法真正找到一种方法。任何帮助表示赞赏。
答案 0 :(得分:1)
如果ID
值是唯一的解决方案:
#filter NaNs rows by UNIT
df1 = df[df['UNIT'].isna()]
print (df1)
ID URINE_TEST UNIT VALUE
5 7 'alb' NaN 1300.0
12 14 'crt' NaN 3.0
13 15 'crt' NaN 99.0
#aggregate min and max values
df2 = df.groupby(['URINE_TEST', 'UNIT']).VALUE.agg(['min','max']).reset_index()
print (df2)
URINE_TEST UNIT min max
0 'alb' g 1.2 1.8
1 'alb' mg 1200.0 1600.0
2 'crt' l 2.3 4.1
3 'crt' ml 2100.0 3400.0
#join together and filter between values, create Series of UNIT values
df3 = df1.merge(df2, on='URINE_TEST', suffixes=('_',''))
s = df3[df3['VALUE'].between(df3['min'], df3['max'])].set_index(['ID'])['UNIT']
print (s)
ID
7 mg
14 l
Name: UNIT, dtype: object
#replace NaNs with s Series
df['UNIT'] = df['ID'].map(s).fillna(df['UNIT'])
或者:
m = df['UNIT'].isna()
df.loc[m, 'UNIT'] = df.loc[m, 'ID'].map(s)
print (df)
ID URINE_TEST UNIT VALUE
0 1 'alb' mg 1500.0
1 2 'alb' mg 1200.0
2 3 'alb' mg 1600.0
3 4 'alb' g 1.2
4 5 'alb' g 1.8
5 7 'alb' mg 1300.0
6 8 'crt' l 2.3
7 9 'crt' l 3.3
8 10 'crt' l 4.1
9 11 'crt' ml 2500.0
10 12 'crt' ml 3400.0
11 13 'crt' ml 2100.0
12 14 'crt' l 3.0
13 15 'crt' NaN 99.0
使用merge
并左联接的解决方案是最通用的:
df1 = df[df['UNIT'].isna()]
df2 = df.groupby(['URINE_TEST', 'UNIT']).VALUE.agg(['min','max']).reset_index()
df3 = df1.merge(df2, on='URINE_TEST', suffixes=('_',''))
df3 = df3.loc[df3['VALUE'].between(df3['min'], df3['max']), ['URINE_TEST','VALUE', 'UNIT']]
df3 = df1.merge(df3, on=['URINE_TEST','VALUE'], suffixes=('_',''), how='left')
print (df3)
ID URINE_TEST UNIT_ VALUE UNIT
0 7 'alb' NaN 1300.0 mg
1 14 'crt' NaN 3.0 l
2 15 'crt' NaN 99.0 NaN
df = (pd.concat([df.dropna(subset=['UNIT']), df3[df.columns]])
.sort_values('URINE_TEST')
.reset_index(drop=True))
print (df)
ID URINE_TEST UNIT VALUE
0 1 'alb' mg 1500.0
1 2 'alb' mg 1200.0
2 3 'alb' mg 1600.0
3 4 'alb' g 1.2
4 5 'alb' g 1.8
5 7 'alb' mg 1300.0
6 8 'crt' l 2.3
7 9 'crt' l 3.3
8 10 'crt' l 4.1
9 11 'crt' ml 2500.0
10 12 'crt' ml 3400.0
11 13 'crt' ml 2100.0
12 14 'crt' l 3.0
13 15 'crt' NaN 99.0
通过df1
中的唯一undex进行匹配的替代项:
df1 = df[df['UNIT'].isna()]
df2 = df.groupby(['URINE_TEST', 'UNIT']).VALUE.agg(['min','max']).reset_index()
#add index to columns by reset_index()
df3 = df1.reset_index().merge(df2, on='URINE_TEST', suffixes=('_',''))
s = df3[df3['VALUE'].between(df3['min'], df3['max'])].set_index(['index'])['UNIT']
print (s)
index
5 mg
12 l
Name: UNIT, dtype: object
df['UNIT'] = df['UNIT'].fillna(s)
print (df)
ID URINE_TEST UNIT VALUE
0 1 'alb' mg 1500.0
1 2 'alb' mg 1200.0
2 3 'alb' mg 1600.0
3 4 'alb' g 1.2
4 5 'alb' g 1.8
5 7 'alb' mg 1300.0
6 8 'crt' l 2.3
7 9 'crt' l 3.3
8 10 'crt' l 4.1
9 11 'crt' ml 2500.0
10 12 'crt' ml 3400.0
11 13 'crt' ml 2100.0
12 14 'crt' l 3.0
13 15 'crt' NaN 99.0
答案 1 :(得分:1)
按照您的逻辑,您只能fillna
使用最小-最大范围内的值,而其他NaN
则保持不变。我认为您可以通过sort_values
,ffill
和loc
分配使用自定义遮罩将NaN
设置回最小-最大范围之外的设置来实现它
df1 = df.sort_values(['VALUE', 'UNIT'])
m1 = df1.UNIT.shift() != df1.UNIT.shift(-1)
m2 = df1.UNIT.isna()
m3 = df1.VALUE != df1.VALUE.shift()
df1['UNIT'] = df1.UNIT.ffill()
df1.loc[m1 & m2 & m3, 'UNIT'] = np.nan
df = df1.reindex(df.index)
Out[130]:
ID URINE_TEST UNIT VALUE
0 1 'alb' mg 1500.0
1 2 'alb' mg 1200.0
2 3 'alb' mg 1600.0
3 4 'alb' g 1.2
4 5 'alb' g 1.8
5 7 'alb' mg 1300.0
6 8 'crt' l 2.3
7 9 'crt' l 3.3
8 10 'crt' l 4.1
9 11 'crt' ml 2500.0
10 12 'crt' ml 3400.0
11 13 'crt' ml 2100.0
12 14 'crt' l 3.0
13 15 'crt' NaN 99.0
答案 2 :(得分:0)
假设我正确理解了您的条件,并且您的值的数据类型为float:
# List for new unit values.
NEW_UNIT = []
# For loop that checks each row in the dataframe for its respective values.
for index, row in df.iterrows():
if row['URINE_TEST'] == 'alb':
if (row['VALUE'] >= 1200) and (row['VALUE'] <= 1600):
NEW_UNIT.append('mg')
elif (row['VALUE'] >= 1.2) and (row['VALUE'] <= 1.6):
NEW_UNIT.append('g')
else:
NEW_UNIT.append(float('NaN'))
elif row['URINE_TEST'] == 'crt':
if (row['VALUE'] >= 2300) and (row['VALUE'] <= 4100):
NEW_UNIT.append('ml')
elif (row['VALUE'] >= 2.3) and (row['VALUE'] <= 4.1):
NEW_UNIT.append('l')
else:
NEW_UNIT.append(float('NaN'))
# Replace unit column with the updated unit values
df['UNIT'] = NEW_UNIT
答案 3 :(得分:0)
您可以使用DataFrame.apply()
函数清除数据并获得所需的结果。您可以在文档中详细了解df.apply()
。
一个粗略的解决方案将看起来像这样,假设数据称为urine_data
:
#create a dictionary of all the tests and their different options and min, max values
test_dic = {'alb': [('mg', 1200, 1800), ('g', 1.2, 1.8)], 'crt': [('l', 2.3, 4.1), ('ml', 2100, 3400)]}
#will be applied for each row in the dataframe
def fill_unit(row):
test = row['URINE_TEST'] #get test
value = row['VALUES'] #get value
unit = row['UNIT'] #get initial unit
if test in test_dic.keys():
if test_dic[test][0][1] <= value <=test_dic[test][0][2]:
unit = test_dic[test][0][0]
elif test_dic[test][1][1] <= value <=test_dic[test][1][2]:
unit = test_dic[test][1][0]
else:
unit = np.nan
return unit
urine_data['UNIT'] = urine_data.apply(fill_unit, axis=1)
这将为您提供输出:
URINE_TEST UNIT VALUES
0 alb mg 1500.0
1 alb mg 1200.0
2 alb mg 1600.0
3 alb g 1.2
4 alb g 1.8
5 alb mg 1300.0
6 crt l 2.3
7 crt l 3.3
8 crt l 4.1
9 crt ml 2500.0
10 crt ml 3400.0
11 crt ml 2100.0
12 crt l 3.0
13 crt NaN 99.0