我有一个带有Date和Merged_ID列的df。
Merged_ID看起来像这样:
18652C1
18652C5
3657C1C2
3657C1C2
3657C3C2
3657C4C2
185000C1
185000C4CC
185000C8CC
它基本上是合并id_1(这是第一部分,主要包含数字,然后是一个或多个id_2。如您所见,id_2有时会与两个id(例如C1和C2)合并。我知道id_2的所有可能性:
C1
C2
C3
CC
C4
C5
C8
我正在寻找一种方法,用这些ID分隔此列,然后将其放在单独的行下。结果应如下所示:
18652C1 // no change
18652C5 // no change
3657C1 // from 3657C1C2
3657C2 // from 3657C1C2
3657C1 // from the second 3657C1C2
3657C2 // from the second 3657C1C2
3657C3 // from 3657C3C2
3657C2 // from 3657C3C2
3657C4 // from 3657C4C2
3657C2 // from 3657C4C2
185000C1 // stays same
185000C4 // from 185000C4CC
185000CC // from 185000C4CC
185000C8 // from 185000C8CC
185000CC // from 185000C8CC
所有这些都使第一列保持完整(日期)。
如何实现列/行拆分?
答案 0 :(得分:1)
这是一个可行的解决方案,在速度方面可能并不是最理想的解决方案,但是这个问题并不常见,因此没有直接的解决方法。
编辑:我检查了速度,它相当不错,因为除了第二行的.apply
以外,所有方法都是矢量化的。
有关详细信息,请参见代码中的注释:
注意:由于我们使用pandas >= 0.25.0
方法,因此您需要.explode
。
# count the amount of C's in each row
c_count = df['Merged_ID'].str.count('C')
# if the amount of C's is 1, then we take the last 2 characters, else the last 4 (which is id2) and inser a space between the two id2's
id2 = pd.Series(np.where(c_count==1, df['Merged_ID'].str[-2:], df['Merged_ID'].str[-4:].apply(lambda x: x[:2]+ ' ' + x[2:])))
# we substract id1 from the string
id1 = df['Merged_ID'].str.split('C\d').str[0]
# we explode the concatenated id2's to different rows and join id1 back
final = id1.to_frame().join(id2.str.split().explode().to_frame())
# finally we concatenate the strings back to each other
final.assign(Merged_ID = final['Merged_ID'].str.cat(final[0])).iloc[:, 0].to_frame()
输出
Merged_ID
0 18652C1
1 18652C5
2 3657C1
2 3657C2
3 3657C1
3 3657C2
4 3657C3
4 3657C2
5 3657C4
5 3657C2
6 185000C1
7 185000C4
7 185000CC
8 185000C8
8 185000CC
答案 1 :(得分:1)
或者,我使用正则表达式解决了它。看看。
import pandas as pd
import re
word_output = []
index_output = []
nice_data = ['18652C1', '18652C5', '3657C1C2', '3657C1C2', '3657C3C2', '3657C4C2', '185000C1', '185000C4CC', '185000C8CC']
for i in range(len(nice_data)):
word = nice_data[i]
matched = re.search(r'(?<=C[0-9A-Z])(C[0-9A-Z]\b)', word)
index_output.append(i)
if matched:
word_output.append(word[:-2]) #e.g. remove C2 in 3657C1C2
word_output.append(word[:-4]+matched.group(1)) #e.g. take 3657+C2 in 3657C1C2
index_output.append(i)
else:
word_output.append(word)
df = pd.DataFrame(index=index_output,data=word_output)
print(df)
输出
0
0 18652C1
1 18652C5
2 3657C1
2 3657C2
3 3657C1
3 3657C2
4 3657C3
4 3657C2
5 3657C4
5 3657C2
6 185000C1
7 185000C4
7 185000CC
8 185000C8
8 185000CC
编辑:这是给您的奖励。我提出了一个具有更强大的正则表达式的解决方案。它可以处理原始数据,也可以处理受特殊字符污染的数据。
import pandas as pd
import re
def extracted(data):
word_output = []
index_output = []
for i in range(len(data)):
word = data[i]
matched = re.findall(r'\W*?(\d*)\W*?(C[0-9A-Z]|)\W*?(C[0-9A-Z]\b)', word)[0]
index_output.append(i)
if matched[1]=='':
word_output.append(matched[0]+matched[2]) #e.g. 18652+C1 in 18652C1
else:
word_output.append(matched[0]+matched[1]) #e.g. 3657+C1 in 3657C1C2
word_output.append(matched[0]+matched[2]) #e.g. 3657+C2 in 3657C1C2
index_output.append(i)
return index_output, word_output
bad_data = ['@&*$&18652C1$@', '^%#18652#%@C5', '#$##3657#$#(C1C2#!&&', '@@#3657C1@#&!C2', '3657C3@&#C2', '3657C4C2@#^*', '185000C1()', '185000&*C4CC', '#%!185000C8CC']
index_output_bad, word_output_bad = extracted(bad_data)
df_bad = pd.DataFrame(index=index_output_bad,data=word_output_bad,columns=['mergedIDs'])
print('Bad Data')
print(df_bad)
nice_data = ['18652C1', '18652C5', '3657C1C2', '3657C1C2', '3657C3C2', '3657C4C2', '185000C1', '185000C4CC', '185000C8CC']
index_output_good, word_output_good = extracted(nice_data)
df_good = pd.DataFrame(index=index_output_good,data=word_output_good,columns=['mergedIDs'])
print('Good (Original) Data')
print(df_good)
输出
Bad Data
mergedIDs
0 18652C1
1 18652C5
2 3657C1
2 3657C2
3 3657C1
3 3657C2
4 3657C3
4 3657C2
5 3657C4
5 3657C2
6 185000C1
7 185000C4
7 185000CC
8 185000C8
8 185000CC
Good (Original) Data
mergedIDs
0 18652C1
1 18652C5
2 3657C1
2 3657C2
3 3657C1
3 3657C2
4 3657C3
4 3657C2
5 3657C4
5 3657C2
6 185000C1
7 185000C4
7 185000CC
8 185000C8
8 185000CC