【问题标题】:Split single Dataframe Column into multiple columns [closed]将单个数据框列拆分为多列[关闭]
【发布时间】:2021-01-30 07:52:06
【问题描述】:

我有以下几行数据(当然比示例多得多)。数据可以以不同的顺序出现。

df = pd.DataFrame({'SmVariant': ['1xFBBC', float('nan'), '2xFBBA', '5xABIA', \
                                '2xFBBC, 1xFBBA', '1xFBBA', '4xABIA', \
                                '1xFBBA, 1xFBBC', float('nan'), '1xFBBA', \
                                '3xFBBA, 1xFBBC']})

我想分成这样的数字列:(最终得到总数)

FBBA  FBBC  ABIA
1           
          
2            
              5
1       2
1
              4
1       1

1
3       1

【问题讨论】:

标签: python regex pandas dataframe split


【解决方案1】:

我假设您的意思是熊猫数据框。我还假设您预先知道不同类型的元素,并且可以像这样将它们放入字典中(将元素映射到最后一列:

cols={'AAAA':0, 'BBBB': 1, 'CCCC': 2}

接下来编写一个将特定元素转换为多列的函数:

def expand_element(el):
  res = [0]*len(cols)
  for item in el.split(','):
    q, name = item.split('x')
    res[cols[name]]=int(q)
  return res

最后,使用该函数并将其应用于数据框中的每个元素,如下所示:

df.apply(lambda x: expand_element(x[0]), axis=1, result_type='expand')

这是我的交互式会话,显示输入和输出:

>>> df=pd.DataFrame({"c1": ["1xAAAA,2xBBBB", "1xAAAA,2xBBBB"]})
>>> df.apply(lambda x: expand_element(x[0]), axis=1, result_type='expand')
   0  1  2
0  1  2  0
1  1  2  0

【讨论】:

  • 这看起来很有趣。我确实想保留“AAAA”、“BBBB”等作为标题。
【解决方案2】:

解决方案

您可以使用regex + pandas method-chaining 在一行中完成,如下所示。为了更好的可读性,我将其分成多行。有关详细信息,请参阅下面的C 部分。 ?⭐

注意:A 节和 B 节使用 D 节中的数据,这些数据是 OP 之前共享的。后来问题中的数据发生了变化,C 部分给出了这个用例的解决方案。

正则表达式示例:

为了解释regex-pattern 的工作原理,请看以下三个示例:

  • example-1:我在Section-A中使用了这个正则表达式模式。鉴于我们拥有 Section-D 中给出的数据,这将有助于工作。
  • example-2:这是 example-1 中正则表达式的改进版本。我在Section-B C中使用了这种正则表达式模式。
  • example-3:最后,此示例说明了示例 2 中使用的模式如何跳过不需要的文本并仅选择预期的文本括号。
# without alphabetically ordering the columns
(df[COLUMN_NAME]                                                   ## access the "data"-column
    .fillna('0xUNKN')                                              ## replace nan values with 0xUNKN 
    .str.findall(pat)                                              ## use regex to extract patterns
    .apply(lambda x: dict((k, v) for v, k in x if (int(v)!=0)))    ## row-wise create dict to construct final {column: count} structure
    .apply(pd.Series)                                              ## use dict to create columns
    .fillna(0)                                                     ## replace NaN values with 0
) 

A.每个操作都有说明

我在这里解释每个操作的作用,最后按字母顺序重新排列列。

正则表达式解释:example-1

在此处查找有关正则表达式 (\d+)x(\w+)\s*,\s*(\d+)x(\w+) 如何从输入文本中提取各种预期部分的详细说明:example-1

# NOTE: I am using the dataframe that I created in 
# the Dummy Data section "below"

df2 = (df.data                                      # access the "data"-column
    .str.findall('(\d+)x(\w+)\s*,\s*(\d+)x(\w+)')   # use regex to extract patterns
    .explode()                                      # explode each rows' list into columns
    .apply(lambda x: {x[1]: x[0], x[3]: x[2]})      # row-wise create dict to construct final {column: count} structure
    .apply(pd.Series)                               # expand each cell into columns
    .fillna(0)                                      # replace NaN values with 0
)
df2 = df2.reindex(sorted(df2.columns), axis=1)      # alphabetically reorder columns
print(df2)

输出

  AAAA BBBB CCCC
0    1    1    0
1    1    2    0
2    1    0    1

B.更通用的解决方案

如果每行有两种以上的类型(例如,AAAABBBBCCCC),则以下解决方案也适用于这种情况。

正则表达式解释:example-2

在此处找到有关正则表达式 (?:\s*(\d+)x(\w+)\s*)+ 如何从输入文本中提取各种预期部分的详细说明:example-2

代码

import pandas as pd

## Dummy Data
data = [
    '1xAAAA,2xBBBB,3xDDDD', 
    '1xBBBB,1xAAAA,6xEEEE', 
    '1xAAAA,1xCCCC,3xDDDD', 
]
df = pd.DataFrame(data, columns=['data'])
print('\n Input:')
print(df)
## Output:
#                    data
# 0  1xAAAA,2xBBBB,3xDDDD
# 1  1xBBBB,1xAAAA,6xEEEE
# 2  1xAAAA,1xCCCC,3xDDDD

## Process DataFrame
# define regex pattern    
pat = '(?:\s*(\d+)x(\w+)\s*)+' # regex search pattern
# create dataframe in the expected format
df2 = (df.data                                      ## access the "data"-column
    .str.findall(pat)                               ## use regex to extract patterns
    .apply(lambda x: dict((k, v) for v, k in x))    ## row-wise create dict to construct final {column: count} structure
    .apply(pd.Series)                               ## use dict to create columns
    .fillna(0)                                      ## replace NaN values with 0
)
df2 = df2.reindex(sorted(df2.columns), axis=1)      ## alphabetically reorder columns
print('\n Output:')
print(df2)

## Output:
#   AAAA BBBB CCCC DDDD EEEE
# 0    1    2    0    3    0
# 1    1    1    0    0    6
# 2    1    0    1    3    0

C. OP 共享数据的具体示例?⭐

这是一个关于 OP 共享的特定样本数据的示例。此特定用例显示数据框中存在 nan 值。作为使用先前建议的解决方案且修改最少的策略,您可以只使用字符串0xUNKNreplace 那些nan 值,然后过滤不以0 开头的结果。

import pandas as pd

COLUMN_NAME = 'SmVariant'

## Dummy Data
data = [
    '1xFBBC', float('nan'), 
    '2xFBBA', '5xABIA', 
    '2xFBBC, 1xFBBA', 
    '1xFBBA', '4xABIA', 
    '1xFBBA, 1xFBBC', 
    float('nan'), '1xFBBA', 
    '3xFBBA, 1xFBBC', 
]
df = pd.DataFrame({COLUMN_NAME: data})
print('\n Input:')
print(df)
## Output:
#          SmVariant
# 0           1xFBBC
# 1              NaN
# 2           2xFBBA
# 3           5xABIA
# 4   2xFBBC, 1xFBBA
# 5           1xFBBA
# 6           4xABIA
# 7   1xFBBA, 1xFBBC
# 8              NaN
# 9           1xFBBA
# 10  3xFBBA, 1xFBBC

## Process DataFrame
# define regex pattern    
pat = '(?:\s*(\d+)x(\w+)\s*)+' # regex search pattern
# create dataframe in the expected format
df2 = (df[COLUMN_NAME]                                             ## access the "data"-column
    .fillna('0xUNKN')                                              ## replace nan values with 0xUNKN 
    .str.findall(pat)                                              ## use regex to extract patterns
    .apply(lambda x: dict((k, v) for v, k in x if (int(v)!=0)))    ## row-wise create dict to construct final {column: count} structure
    .apply(pd.Series)                                              ## use dict to create columns
    .fillna(0)                                                     ## replace NaN values with 0
)
df2 = df2.reindex(sorted(df2.columns), axis=1)                     ## alphabetically reorder columns
print('\n Output:')
print(df2)

## Output:
#    ABIA FBBA FBBC
# 0     0    0    1
# 1     0    0    0
# 2     0    2    0
# 3     5    0    0
# 4     0    1    2
# 5     0    1    0
# 6     4    0    0
# 7     0    1    1
# 8     0    0    0
# 9     0    1    0
# 10    0    3    1

D.虚拟数据

import pandas as pd

data = {
    '1xAAAA,2xBBBB', 
    '1xBBBB,1xAAAA', 
    '1xAAAA,1xCCCC', 
}
df = pd.DataFrame(data, columns=['data'])
print(df)
## Output:
#             data
# 0  1xBBBB,1xAAAA
# 1  1xAAAA,2xBBBB
# 2  1xAAAA,1xCCCC

参考文献

  1. Re-ordering columns in pandas dataframe based on column name [duplicate]

  2. pandas.DataFrame.explode

  3. Expand Cells Containing Lists Into Their Own Variables In Pandas

【讨论】:

  • @Wayne_AB 如果您有任何问题,请告诉我。我希望这会有所帮助。
  • 谢谢。当我有时间时,我会研究解决方案。你能解释一下正则表达式吗?
  • @Wayne_AB 我在解决方案中添加了两个示例:第一个将逐步解释第一个模式,第二个(这是第一个更简洁和通用的版本),将向您解释其中的每个步骤。 example-1example-2.
  • 这里有一个更好的示例数据:df = pd.DataFrame({'SmVariant': ['1xFBBC', float('nan'), '2xFBBA', '5xABIA', \ '2xFBBC, 1xFBBA', '1xFBBA', '4xABIA', \'1xFBBA, 1xFBBC', float('nan'), '1xFBBA', \'3xFBBA, 1xFBBC']})
  • 谢谢。回复让我 110% 震惊。我是一名多年没有做过任何编码的电子工程师。我在很多个月前开始使用 Pascal,最近使用了 VisualBasic,它对于我的目的来说太弱且过时了。这是一个很棒的资源,我非常感激。感谢 CypherX。
猜你喜欢
  • 2020-12-21
  • 1970-01-01
  • 1970-01-01
  • 2016-10-13
  • 2023-03-10
  • 1970-01-01
  • 2017-09-12
  • 1970-01-01
  • 2021-03-27
相关资源
最近更新 更多