Python 在宽数据框内将3个文本栏折叠为1个
我有一个数据集,其中一种数据类型分布在多个列中。我想把它们简化成一列。我有一个函数可以实现这一点,但这是一个繁琐的过程,我希望有一个更干净的方法来实现这一点。这是我的数据的一个玩具样本:Python 在宽数据框内将3个文本栏折叠为1个,python,pandas,dataframe,reduction,Python,Pandas,Dataframe,Reduction,我有一个数据集,其中一种数据类型分布在多个列中。我想把它们简化成一列。我有一个函数可以实现这一点,但这是一个繁琐的过程,我希望有一个更干净的方法来实现这一点。这是我的数据的一个玩具样本: UID COMPANY EML MAI TEL 273 7UP nan nan TEL 273 7UP nan MAI nan 906 WSJ nan nan TEL 906 WSJ
UID COMPANY EML MAI TEL
273 7UP nan nan TEL
273 7UP nan MAI nan
906 WSJ nan nan TEL
906 WSJ EML nan nan
736 AIG nan MAI nan
我想得到的是:
UID COMPANY CONTACT_INFO
273 7UP MT
906 WSJ ET
736 AIG M
我通过编写一个函数来解决这个问题,该函数将EML
、MAI
或TEL
转换为素数,聚合结果,然后将总和转换为组成触点类型。这是可行的,而且相当快。以下是一个示例:
def columnRedux(df):
newDF = df.copy()
newDF.fillna('-', inplace=True)
newDF['CONTACT_INFO'] = newDF['EML'] + newDF['MAI'] + newDF['TEL']
newDF.replace('EML--', 7, inplace=True)
newDF.replace('-MAI-', 101, inplace=True)
newDF.replace('--TEL', 1009, inplace=True)
small = newDF.groupby(['UID', 'COMPANY'], as_index=False)['CONTACT_INFO'].sum()
small.replace(7, 'E', inplace=True)
small.replace(101, 'M', inplace=True)
small.replace(108, 'EM', inplace=True)
small.replace(1009, 'T', inplace=True)
small.replace(1016, 'ET', inplace=True)
small.replace(1110, 'MT', inplace=True)
small.replace(1117, 'EMT', inplace=True)
return small
df1 = pd.DataFrame(
{'EML' : [np.nan, np.nan, np.nan, 'EML', np.nan, np.nan, 'EML', np.nan, np.nan, 'EML', 'EML', np.nan],
'MAI' : [np.nan, 'MAI', np.nan, np.nan, 'MAI', np.nan, np.nan, np.nan, 'MAI', np.nan, np.nan, 'MAI'],
'COMPANY' : ['7UP', '7UP', 'UPS', 'UPS', 'UPS', 'WSJ', 'WSJ', 'TJX', 'AIG', 'CDW', 'HEB', 'HEB'],
'TEL' : ['TEL', np.nan, 'TEL', np.nan, np.nan, 'TEL', np.nan, 'TEL', np.nan, np.nan, np.nan, np.nan],
'UID' : [273, 273, 865, 865, 865, 906, 906, 736, 316, 458, 531, 531]},
columns=['UID', 'COMPANY', 'EML', 'MAI', 'TEL'])
cleanDF = columnRedux(df1)
我的问题是我有几个数据集,每个数据集都有自己的“宽”列集。有些需要减少5+列。对所有变体的转换进行硬编码并非易事。有没有更干净的方法来实现这一点?也许不是“最好的”解决方案。但一种方法是使用简单的groupby并对包含的元素进行条件设置:
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
.apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
.reset_index()\
.rename(columns={0:'CONTACT_INFO'})
或者,另一种方法是将分组的数据帧转换为str类型,并替换字符串和sum。我想说,相当可读
m = {
'nan':'',
'EML':'E',
'MAI':'M',
'TEL':'T'
}
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
.apply(lambda x: x.astype(str).replace(m).sum().sum())\
.reset_index()\
.rename(columns={0:'CONTACT_INFO'})
完整示例:
import pandas as pd
import numpy as np
data = '''\
UID COMPANY EML MAI TEL
273 7UP nan nan TEL
273 7UP nan MAI nan
906 WSJ nan nan TEL
906 WSJ EML nan nan
736 AIG nan MAI nan'''
fileobj = pd.compat.StringIO(data)
df = pd.read_csv(fileobj, sep='\s+').replace('NaN',np.nan)
# use a nested list comprehension to flatten the array and remove nans.
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
.apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
.reset_index()\
.rename(columns={0:'CONTACT_INFO'})
print(df)
返回:
UID COMPANY CONTACT_INFO
273 7UP MT
736 AIG M
906 WSJ ET
dtype: object
也许不是“最好的”解决方案。但一种方法是使用简单的groupby并对包含的元素进行条件设置:
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
.apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
.reset_index()\
.rename(columns={0:'CONTACT_INFO'})
或者,另一种方法是将分组的数据帧转换为str类型,并替换字符串和sum。我想说,相当可读
m = {
'nan':'',
'EML':'E',
'MAI':'M',
'TEL':'T'
}
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
.apply(lambda x: x.astype(str).replace(m).sum().sum())\
.reset_index()\
.rename(columns={0:'CONTACT_INFO'})
完整示例:
import pandas as pd
import numpy as np
data = '''\
UID COMPANY EML MAI TEL
273 7UP nan nan TEL
273 7UP nan MAI nan
906 WSJ nan nan TEL
906 WSJ EML nan nan
736 AIG nan MAI nan'''
fileobj = pd.compat.StringIO(data)
df = pd.read_csv(fileobj, sep='\s+').replace('NaN',np.nan)
# use a nested list comprehension to flatten the array and remove nans.
df = df.groupby(['UID','COMPANY'])[['EML','MAI','TEL']]\
.apply(lambda x: ''.join(sorted([i[0] for y in x.values for i in y if pd.notnull(i)])))\
.reset_index()\
.rename(columns={0:'CONTACT_INFO'})
print(df)
返回:
UID COMPANY CONTACT_INFO
273 7UP MT
736 AIG M
906 WSJ ET
dtype: object
让我们试试这个:
(df1.set_index(['UID','COMPANY']).notnull() * df1.columns[2:].str[0])\
.sum(level=[0,1]).sum(1).reset_index(name='CONTACT_INFO')
输出:
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
拆分为@AntonvBR:
df2 = df1.set_index(['UID','COMPANY'])
df_out = ((df2.notnull() * df2.columns.str[0])
.sum(level=[0,1]) #consolidate rows of contact info to one line
.sum(1) #sum across columns to create one column
.reset_index(name='CONTACT_INFO'))
print(df_out)
输出:
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
让我们试试这个:
(df1.set_index(['UID','COMPANY']).notnull() * df1.columns[2:].str[0])\
.sum(level=[0,1]).sum(1).reset_index(name='CONTACT_INFO')
输出:
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
拆分为@AntonvBR:
df2 = df1.set_index(['UID','COMPANY'])
df_out = ((df2.notnull() * df2.columns.str[0])
.sum(level=[0,1]) #consolidate rows of contact info to one line
.sum(1) #sum across columns to create one column
.reset_index(name='CONTACT_INFO'))
print(df_out)
输出:
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 865 UPS EMT
2 906 WSJ ET
3 736 TJX T
4 316 AIG M
5 458 CDW E
6 531 HEB EM
通过使用
dot
在groupby
第一列之后创建新列
s=df.groupby(['UID','COMPANY'],as_index=False).first()
s['CONTACT_INFO']=s[['EML','MAI','TEL']].notnull().dot(s.columns[2:].str[0])
s.dropna(1)
Out[349]:
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 736 AIG M
2 906 WSJ ET
通过使用
dot
在groupby
第一列之后创建新列
s=df.groupby(['UID','COMPANY'],as_index=False).first()
s['CONTACT_INFO']=s[['EML','MAI','TEL']].notnull().dot(s.columns[2:].str[0])
s.dropna(1)
Out[349]:
UID COMPANY CONTACT_INFO
0 273 7UP MT
1 736 AIG M
2 906 WSJ ET
好的,是的,现在我们在谈。不知道为什么要使用
s
,因为这意味着你有一个系列。dropna(1)不是100%稳定的。很好的解决方案。好的,是的,现在我们在谈。不知道为什么要使用s
,因为这意味着你有一个系列。dropna(1)不是100%稳定的。很好的解决方案,这很好。我也不知道我可以用这种方式从字符串中干净地创建DF。谢谢你的帮助。我把这个作为答案。它解决了我所有的问题,我相信你是第一个回答的。@Lenwood是的,我可能是第一个。但我要说的是,你应该投票选出最适合你的候选人。其他解决方案也很聪明:)这很好。我也不知道我可以用这种方式从字符串中干净地创建DF。谢谢你的帮助。我把这个作为答案。它解决了我所有的问题,我相信你是第一个回答的。@Lenwood是的,我可能是第一个。但我要说的是,你应该投票选出最适合你的候选人。其他解决方案也相当聪明:)