如何基于合并的列拆分成多行

时间:2019-10-02 12:15:22

标签: python pandas

我有一个带有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

所有这些都使第一列保持完整(日期)。

如何实现列/行拆分?

2 个答案:

答案 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