3

我正在研究 pandas 以对 NLP 和文本挖掘进行一些简单的计算,但我无法完全掌握如何去做。

假设我有以下数据框,将人们的姓名和性别联系起来:

import pandas
people = {'name': ['John Doe', 'Mary Poppins', 'Jane Doe', 'John Cusack'], 'gender': ['M', 'F', 'F', 'M']}
df = pandas.DataFrame(people)

对于我想要的所有行:

  1. 确定名字
  2. 确定从人名派生的 3-shingles 列表(单词中包含 3 个字母的序列)
  3. 确定,对于每个带状疱疹,有多少男性和女性在他们的名字上包含该带状疱疹。

目标是将其用作数据集来训练分类器,该分类器可以确定给定名称是男性名字还是女性名字。

前两个操作非常简单:

def shingles(word, n = 3):
    return [word[i:i + n] for i in range(len(word) - n + 1)]

df['firstname'] = df.name.map(lambda x : x.split()[0])
df['shingles'] = df.firstname.map(shingles)

结果是:

> print df


  gender          name firstname        shingles
0      M      John Doe      John  ['joh', 'ohn']
1      F  Mary Poppins      Mary  ['mar', 'ary']
2      F      Jane Doe      Jane  ['jan', 'ane']
3      M   John Cusack      John  ['joh', 'ohn']

现在,下一步应该通过构建一个包含两列的新数据框来完成:gender 和 shingle,其中应该包含以下内容:

   gender      shingle 
0       M          joh
1       M          ohn
2       F          mar
3       F          ary
(...)

然后我可以按带状疱疹和性别分组。理想情况下,结果将是:

   shingle    num_males  num_females 
0      joh            2            0 
1      ohn            2            0 
2      mar            0            1 
3      ary            0            1
(...)

有没有一种简单的方法来扩展多值列shingles,每行产生多行,一个用于在带状疱疹列表中找到的每个值?

另外,如果我groupby是列shingle,为列的每个可能值生成不同的列有多容易gender


我设法理解了第二部分。例如,要计算每个 有多少男性和女性firstname

 def countMaleFemale(df): 
     return pandas.Series({'males': df.gender[df.gender == 'M'].count(), 
                           'females': df.gender[df.gender == 'F'].count()})

grouped = df.groupby('first name')

接着:

打印 grouped.apply(countMaleFemale)

            females  males
first name                
Jane              1      0
John              0      2
Mary              1      0
4

2 回答 2

7

这种方法应该可以很好地概括:

In [100]: df
Out[100]:
  gender          name firstname    shingles
0      M      John Doe      John  [Joh, ohn]
1      F  Mary Poppins      Mary  [Mar, ary]
2      F      Jane Doe      Jane  [Jan, ane]
3      M   John Cusack      John  [Joh, ohn]

首先创建一个“扩展”系列,其中每个条目都是一个木瓦。这里,系列的索引是一个多重索引,其中第一级表示瓦的位置,第二级表示原始 DF 的索引:

In [103]: s = df.shingles.apply(lambda x: pandas.Series(x)).unstack();
Out[103]:
0  0    Joh
   1    Mar
   2    Jan
   3    Joh
1  0    ohn
   1    ary
   2    ane
   3    ohn

接下来,我们可以将创建的系列加入到原始数据框中。您必须重置索引,降低 shingle 位置级别。结果系列具有原始索引和每个带状疱疹的条目。将其合并到原始数据框中会产生:

In [106]: df2 = df.join(pandas.DataFrame(s.reset_index(level=0, drop=True))); df2
Out[106]:
  gender          name firstname    shingles    0
0      M      John Doe      John  [Joh, ohn]  Joh
0      M      John Doe      John  [Joh, ohn]  ohn
1      F  Mary Poppins      Mary  [Mar, ary]  Mar
1      F  Mary Poppins      Mary  [Mar, ary]  ary
2      F      Jane Doe      Jane  [Jan, ane]  Jan
2      F      Jane Doe      Jane  [Jan, ane]  ane
3      M   John Cusack      John  [Joh, ohn]  Joh
3      M   John Cusack      John  [Joh, ohn]  ohn

最后,您可以对 Gender 执行 groupby 操作,取消堆叠返回的系列并用零填充 NaN:

In [124]: df2.groupby(0, sort=False)['gender'].value_counts().unstack().fillna(0)
Out[124]:
     F  M
0
Joh  0  2
ohn  0  2
Mar  1  0
ary  1  0
Jan  1  0
ane  1  0
于 2013-03-07T17:20:39.167 回答
2

在创建时创建扩展版本可能更容易shingles这个问题展示了如何使用 groupby 进行这种扩展。以下是创建“名字”列后可以执行的操作的示例:

def shingles(table, n = 3):
    word = table['first name'].irow(0)
    shingles = [word[i:i + n] for i in range(len(word) - n + 1)]
    cols = {col: table[col].irow(0) for col in table.columns}
    cols['shingle'] = shingles
    return pandas.DataFrame(cols)

>>> df.groupby('name', group_keys=False).apply(shingles)
  first name gender          name shingle
0       Jane      F      Jane Doe     Jan
1       Jane      F      Jane Doe     ane
0       John      M   John Cusack     Joh
1       John      M   John Cusack     ohn
0       John      M      John Doe     Joh
1       John      M      John Doe     ohn
0       Mary      F  Mary Poppins     Mar
1       Mary      F  Mary Poppins     ary

(我在这里按名字而不是名字分组,以防万一有重复的名字,但它假设全名是唯一的。)

从那里你应该能够分组和计算你喜欢的任何东西。

于 2013-03-06T18:45:46.673 回答