Python 输入数据帧计数行值

Python 输入数据帧计数行值,python,pandas,dataframe,Python,Pandas,Dataframe,我有一个单词表,如下所示 df = pd.DataFrame({'id' : [1,2,3,4], 'path' : ["p1,p2,p3,p4","p1,p2,p1","p1,p5,p5,p7","p1,p2,p3,p3"]}) 单词表=['p1'、'p2'、'p3'、'p4'、'p5'、'p6'、'p7'] 数据帧如下所示 df = pd.DataFrame({'id' : [1,2,3,4], 'path' : ["p1

我有一个单词表,如下所示

df = pd.DataFrame({'id' : [1,2,3,4],
                'path'  : ["p1,p2,p3,p4","p1,p2,p1","p1,p5,p5,p7","p1,p2,p3,p3"]})
单词表=['p1'、'p2'、'p3'、'p4'、'p5'、'p6'、'p7']

数据帧如下所示

df = pd.DataFrame({'id' : [1,2,3,4],
                'path'  : ["p1,p2,p3,p4","p1,p2,p1","p1,p5,p5,p7","p1,p2,p3,p3"]})
输出:

    id path

    1 p1,p2,p3,p4
    2 p1,p2,p1
    3 p1,p5,p5,p7
    4 p1,p2,p3,p3
我想计算路径数据以获得以下输出。有可能实现这种转变吗

id p1 p2 p3 p4 p5 p6 p7
1  1  1  1  1  0  0  0
2  2  1  0  0  0  0  0
3  1  0  0  0  2  0  1
4  1  1  2  0  0  0  0

类似于此:

df1 = pd.DataFrame([[path.count(p) for p in wordlist] for path in df['path']],columns=['p1','p2','p3','p4','p5','p6','p7'])

您可以使用矢量化字符串方法
str.count()
(请参阅和),对于单词列表中的每个元素,将其馈送到新的数据帧:

In [4]: pd.DataFrame({name : df["path"].str.count(name) for name in wordlist})
Out[4]:
    p1  p2  p3  p4  p5  p6  p7
id
1    1   1   1   1   0   0   0
2    2   1   0   0   0   0   0
3    1   0   0   0   2   0   1
4    1   1   2   0   0   0   0

更新:对评论的一些回答。事实上,如果字符串可以是彼此的子字符串,那么这将不起作用(但是OP应该澄清这一点)。如果是这样的话,这将起作用(而且速度更快):

还有一些测试来支持我关于更快的说法:-)
当然,我不知道实际的用例是什么,但我把数据帧放大了一点(只是重复了1000次,差别就更大了):


我还在
wordlist
中使用了更多的元素进行了测试,得出的结论是:如果你在
wordlist
中有一个较大的数据框和相对较少的元素,我的方法会更快,如果你有一个较大的
wordlist
,那么使用@RomanPekar中的
计数器的方法会更快(但只有最后一个).

我认为这会很有效

# create Series with dictionaries
>>> from collections import Counter
>>> c = df["path"].str.split(',').apply(Counter)
>>> c
0    {u'p2': 1, u'p3': 1, u'p1': 1, u'p4': 1}
1                        {u'p2': 1, u'p1': 2}
2              {u'p1': 1, u'p7': 1, u'p5': 2}
3              {u'p2': 1, u'p3': 2, u'p1': 1}

# create DataFrame
>>> pd.DataFrame({n: c.apply(lambda x: x.get(n, 0)) for n in wordlist})
   p1  p2  p3  p4  p5  p6  p7
0   1   1   1   1   0   0   0
1   2   1   0   0   0   0   0
2   1   0   0   0   2   0   1
3   1   1   2   0   0   0   0
更新 另一种方法是:

>>> dfN = df["path"].str.split(',').apply(lambda x: pd.Series(Counter(x)))
>>> pd.DataFrame(dfN, columns=wordlist).fillna(0)
   p1  p2  p3  p4  p5  p6  p7
0   1   1   1   1   0   0   0
1   2   1   0   0   0   0   0
2   1   0   0   0   2   0   1
3   1   1   2   0   0   0   0
更新2 一些粗略的性能测试:

>>> dfL = pd.concat([df]*100)
>>> timeit('c = dfL["path"].str.split(",").apply(Counter); d = pd.DataFrame({n: c.apply(lambda x: x.get(n, 0)) for n in wordlist})', 'from __main__ import dfL, wordlist; import pandas as pd; from collections import Counter', number=100)
0.7363274283027295

>>> timeit('splitted = dfL["path"].str.split(","); d = pd.DataFrame({name : splitted.apply(lambda x: x.count(name)) for name in wordlist})', 'from __main__ import dfL, wordlist; import pandas as pd', number=100)
0.5305424618886718

# now let's make wordlist larger
>>> wordlist = wordlist + list(lowercase) + list(uppercase)

>>> timeit('c = dfL["path"].str.split(",").apply(Counter); d = pd.DataFrame({n: c.apply(lambda x: x.get(n, 0)) for n in wordlist})', 'from __main__ import dfL, wordlist; import pandas as pd; from collections import Counter', number=100)
1.765344003293876

>>> timeit('splitted = dfL["path"].str.split(","); d = pd.DataFrame({name : splitted.apply(lambda x: x.count(name)) for name in wordlist})', 'from __main__ import dfL, wordlist; import pandas as pd', number=100)
2.33328927599905
更新3 阅读之后,我发现
计数器
非常慢。您可以使用
defaultdict
对其进行一些优化:

>>> def create_dict(x):
...     d = defaultdict(int)
...     for c in x:
...         d[c] += 1
...     return d
>>> c = df["path"].str.split(",").apply(create_dict)
>>> pd.DataFrame({n: c.apply(lambda x: x[n]) for n in wordlist})
   p1  p2  p3  p4  p5  p6  p7
0   1   1   1   1   0   0   0
1   2   1   0   0   0   0   0
2   1   0   0   0   2   0   1
3   1   1   2   0   0   0   0
以及一些测试:

>>> timeit('c = dfL["path"].str.split(",").apply(create_dict); d = pd.DataFrame({n: c.apply(lambda x: x[n]) for n in wordlist})', 'from __main__ import dfL, wordlist, create_dict; import pandas as pd; from collections import defaultdict', number=100)
0.45942801555111146

# now let's make wordlist larger
>>> wordlist = wordlist + list(lowercase) + list(uppercase)
>>> timeit('c = dfL["path"].str.split(",").apply(create_dict); d = pd.DataFrame({n: c.apply(lambda x: x[n]) for n in wordlist})', 'from __main__ import dfL, wordlist, create_dict; import pandas as pd; from collections import defaultdict', number=100)
1.5798653213942089

这在示例中有效,但在实践中可能是一个糟糕的想法,因为某些单词将被重复计算(例如“cat”中的“at”)。希望这个问题的修改能解决这个问题。@joris:这也可以应用,但对于我真正的问题,我使用了Roman Pekar的方法。谢谢:)@U2EF1是的,无论如何,这种方法对我来说都是无效的,因为它多次在字符串上运行。这就是我建议首先拆分字符串的原因。@RomanPekar注意,我的解决方案速度更快,尽管您可能觉得它不够有效。我个人认为它更简单。@joris很高兴知道,可能会添加一些测试?:)我认为如果字数太多,效率可能会降低。并不是我不喜欢这个解决方案,只是不喜欢反复迭代字符串的想法。
wordlist
中的单词真的就这么简单吗,或者它们可以是彼此的子字符串吗?即使它们可以是子字符串,下面的答案也有95%是你想要的。我的问题没有我问的那么简单。这就是为什么花了一点时间来接受这个答案。谢谢:)请注意,如果您没有更大的单词列表(或者与单词列表相比没有相对较大的数据帧),那么即使您的上一个解决方案也比我的慢。我想说的是,取决于具体的实际用例:-),我喜欢我的简单性。@joris同意简单性,可读性也很重要。如果我有时间,我会考虑使用字典的简单解决方案,我认为应该有一个:)
>>> timeit('c = dfL["path"].str.split(",").apply(create_dict); d = pd.DataFrame({n: c.apply(lambda x: x[n]) for n in wordlist})', 'from __main__ import dfL, wordlist, create_dict; import pandas as pd; from collections import defaultdict', number=100)
0.45942801555111146

# now let's make wordlist larger
>>> wordlist = wordlist + list(lowercase) + list(uppercase)
>>> timeit('c = dfL["path"].str.split(",").apply(create_dict); d = pd.DataFrame({n: c.apply(lambda x: x[n]) for n in wordlist})', 'from __main__ import dfL, wordlist, create_dict; import pandas as pd; from collections import defaultdict', number=100)
1.5798653213942089