如何在Pandas数据框中使用apply返回多列

4

我正在尝试将一个函数应用到Pandas dataframe的一列中,该函数返回一个元组列表。 这是我的函数:

def myfunc(text):
  values=[]
  sections=api_call(text)
  for (part1, part2, part3) in sections:
    value=(part1, part2, part3) 
    values.append(value)
  return values

例如,
sections=myfunc("History: Had a fever\n Allergies: No")
print(sections)

输出:

[('past_medical_history', 'History:', 'History: Had a fever\n '), ('allergies', 'Allergies:', 'Allergies: No')]

对于每个元组,我想创建一个新的列。例如:

原始数据框如下所示:

id text
0  History: Had a fever\n Allergies: No
1  text2

在应用该函数之后,我希望数据框看起来像这样(其中 xxx 是各种文本内容):

id text            part1        part2        part3
0  History: Had... past_...     History:     History: ...
0  Allergies: No   allergies    Allergies:   Allergies: No
1  text2           xxx          xxx          xxx
1  text2           xxx          xxx          xxx
1  text2           xxx          xxx          xxx
...

我可以遍历数据框并生成一个新的数据框,但这样做会非常慢。我尝试了下面的代码,但是收到了一个 ValueError 的错误信息。有什么建议吗?

df.apply(lambda x: pd.Series(myfunc(x['col']), index=['part1', 'part2', 'part3']), axis=1)

我做了更多的研究,所以我的问题实际上归结为如何将一个包含元组列表的列展开。我在这个链接Split a list of tuples in a column of dataframe to columns of a dataframe找到了答案。以下是我所做的:

# step1: sectionizing
df["sections"] =df["text"].apply(myfunc)

# step2: unnest the sections 
part1s = []
part2s = []
part3s = []
ids = []

def create_lists(row):
    tuples = row['sections']
    id = row['id']
    for t in tuples:
        part1s.append(t[0])
        part2s.append(t[1])
        part3s.append(t[2])
        ids.append(id)

df.apply(create_lists, axis=1)

new_df = pd.DataFrame({"part1" :part1s, "part2": part2s, "part3": part3s, 
                       "id": ids})[["part1", "part2", 'part3', "id"]]

但是性能并不太好。我想知道是否有更好的方法。


df[['part1', 'part2', 'part3']] = df['text'].apply(myfunc) 应该可以工作 - Asish M.
你也可以使用 df[['part1', 'part2', 'part3']] = df['names'].str.split(',',expand=True) 进行操作。 - Joe Ferndz
是的。该函数实际上将文本列分成几个部分。不同的文本可能有不同数量的部分。 - GLP
@GLP,如果有帮助的话,请看我的答案变化。 - Joe Ferndz
@GLP,更新了我的答案以解决元组问题。 - Joe Ferndz
显示剩余2条评论
2个回答

1
这里的想法是设置一些数据和一个函数,可以对这些数据进行操作以生成三个我们可以返回的项目。选择分隔符和逗号分隔的值似乎很快并且反映了您所需要的功能。
import pandas as pd
data = { 'names' : ['x,a,c','y,er,rt','z,1,ere']}
df = pd.DataFrame(data)

提供

     names
0    x,a,c
1  y,er,rt
2  z,1,ere

现在
def myfunc(text):
  sections=text.split(',')
  return sections

df[['part1', 'part2', 'part3']] = df['names'].apply(myfunc)

将会给予。
    names   part1   part2   part3
0   x,a,c   x       y       z
1   y,er,rt a       er      1
2   z,1,ere c       rt      ere

这可能不是你想要的,但是

df['part1'] ,df['part2'], df['part3'] = zip(*df['names'].apply(myfunc))

提供

     names     part1 part2 part3
0    x,a,c     x     a     c
1  y,er,rt     y     er    rt
2  z,1,ere     z     1     ere

这可能是你想要的内容。

1

将元组转换为新列:

要将元组列值转换为新列,可以执行以下操作:

df[['part1', 'part2', 'part3']] = pd.DataFrame(df['text'].tolist())
print (df)

这段话的翻译是:“这个的输出将会是:”。
                                                text                 part1  \
0  (past_medical_history, History:, History: Had ...  past_medical_history   
1             (allergies, Allergies:, Allergies: No)             allergies   

        part2                    part3  
0    History:  History: Had a fever\n   
1  Allergies:            Allergies: No  

如果 df['text'] 中的元组是变化的(不是固定的3个项),那么您可以按以下方式连接:
df = pd.concat([df[['text']],pd.DataFrame(df['text'].tolist()).add_prefix('part')],axis=1)

这将给你与之前相同的结果。列名略有不同。

将列中的逗号分隔值转换为单独的列

您不需要使用函数来完成此操作。您已经拥有一个pd.Series。您所要做的就是拆分和扩展。

df[['part1', 'part2', 'part3']] = df['names'].str.split(',',expand=True)

这段话的翻译是:“输出将会是:”。
     names part1 part2 part3
0    a,b,c     a     b     c
1    e,f,g     e     f     g
2    x,y,z     x     y     z

如果您在 names 列中有奇数个值,并且希望将它们分成 3 部分,可以按照以下方式操作:
在拆分过程中,您可以指定要将其拆分为多少列。n 的值设置为拆分为 n 部分(从 0 开始。如果您需要 3 列,则 n=2)。
import pandas as pd
data = { 'names' : ['a,b,c','d,e,f','p,q,r,s','x,y,z']}
df = pd.DataFrame(data)
df = pd.concat([df[['names']],df['names'].str.split(',',n=2,expand=True).add_prefix('part')],axis=1)
print (df)

输出结果将会是:

     names part0 part1 part2
0    a,b,c     a     b     c
1    d,e,f     d     e     f
2  p,q,r,s     p     q   r,s
3    x,y,z     x     y     z

或者您也可以按照以下方式进行:
df[['part1', 'part2', 'part3']] = df['names'].str.split(',',n=2,expand=True)

这将给你与下面相同的结果:
     names part1 part2 part3
0    a,b,c     a     b     c
1    d,e,f     d     e     f
2  p,q,r,s     p     q   r,s
3    x,y,z     x     y     z

如果您想将所有值拆分到每个列中,则可以执行以下操作:

df = pd.concat([df[['names']],df['names'].str.split(',',expand=True).add_prefix('part').fillna('')],axis=1)

这段话的意思是:“这将产生的输出为:”。
     names part0 part1 part2 part3
0    a,b,c     a     b     c      
1    d,e,f     d     e     f      
2  p,q,r,s     p     q     r     s
3    x,y,z     x     y     z      

你可以选择使用np.nan来存储NaN值。
如果你需要考虑多个分隔符并拆分列,则可以使用以下方法。
import pandas as pd
data = { 'names' : ['a,b,c','d,e,f','p;q,r,s','x,y\nz,w']}
df = pd.DataFrame(data)
df = pd.concat([df[['names']],df['names'].str.split(',|\n|;',expand=True).add_prefix('part').fillna('')],axis=1)
print (df)

输出结果如下:
      names part0 part1 part2 part3
0     a,b,c     a     b     c      
1     d,e,f     d     e     f      
2   p;q,r,s     p     q     r     s
3  x,y\nz,w     x     y     z     w

抱歉,我没有清楚地描述我的问题。我的列不像上面的“names”那样,它是一个元组列表,列表的长度是可变的。 - GLP
@GLP,请看看新的更新是否适用于您。您需要将其转换为tolist(),它就可以解决问题了。 - Joe Ferndz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接