【问题标题】:Split series containing lists of strings into multiple columns将包含字符串列表的系列拆分为多列
【发布时间】:2013-09-05 16:39:27
【问题描述】:

我正在使用 pandas 从 Twitter 数据集中执行一些字符串匹配。

我已导入 CSV 推文并使用日期编制索引。然后我创建了一个包含文本匹配的新列:

In [1]:
import pandas as pd
indata = pd.read_csv('tweets.csv')
indata.index = pd.to_datetime(indata["Date"])
indata["matches"] = indata.Tweet.str.findall("rudd|abbott")
only_results = pd.Series(indata["matches"])
only_results.head(10)

Out[1]:
Date
2013-08-06 16:03:17          []
2013-08-06 16:03:12          []
2013-08-06 16:03:10          []
2013-08-06 16:03:09          []
2013-08-06 16:03:08          []
2013-08-06 16:03:07          []
2013-08-06 16:03:07    [abbott]
2013-08-06 16:03:06          []
2013-08-06 16:03:02          []
2013-08-06 16:03:00      [rudd]
Name: matches, dtype: object

我最终想要的是一个按日/月分组的数据框,我可以将不同的搜索词绘制为列然后进行绘图。

我在另一个 SO 答案 (https://stackoverflow.com/a/16637607/2034487) 上遇到了看似完美的解决方案,但在尝试申请这个系列时,我遇到了一个例外:

In [2]: only_results.apply(lambda x: pd.Series(1,index=x)).fillna(0)
Out [2]: Exception - Traceback (most recent call last)
...
Exception: Reindexing only valid with uniquely valued Index objects

我真的希望能够应用数据框中的更改来应用和重新应用 groupby 条件并有效地执行绘图 - 并且很想了解有关 .apply() 方法如何工作的更多信息。

提前致谢。

成功回答后更新

问题在于我没有看到的“匹配”列中的重复项。我遍历该列以删除重复项,然后使用上面链接的@Jeff 的原始解决方案。这很成功,我现在可以在结果系列上使用 .groupby() 来查看每日、每小时等趋势。以下是结果图的示例:

In [3]: successful_run = only_results.apply(lambda x: pd.Series(1,index=x)).fillna(0)
In [4]: successful_run.groupby([successful_run.index.day,successful_run.index.hour]).sum().plot()

Out [4]: <matplotlib.axes.AxesSubplot at 0x110b51650>

【问题讨论】:

    标签: pandas


    【解决方案1】:

    您有一些重复的结果(例如,Rudd 在一条推文中出现多次),因此出现异常(见下文)。

    我认为计算出现次数而不是 findall 中的列表会更好(pandas 数据结构并非旨在包含列表,尽管 str.findall 使用它们)。
    我建议使用这样的东西:

    In [1]: s = pd.Series(['aa', 'aba', 'b'])
    
    In [2]: pd.DataFrame({key: s.str.count(key) for key in ['a', 'b']})
    Out[2]: 
       a  b
    0  2  0
    1  2  1
    2  0  1
    

    注意(由于在前两行中发现重复的'a'而导致异常):

    In [3]: s.str.findall('a').apply(lambda x: pd.Series(1,index=x)).fillna(0)
    #InvalidIndexError: Reindexing only valid with uniquely valued Index objects
    

    【讨论】:

    • 谢谢@Andy - 我今天早上会运行代码(我在悉尼),一旦我被排序,就标记为接受。
    • 我已将此标记为已接受,因为它指出了主要问题 - lambda 函数中的重复项。我实际上并不需要重复项,它们只是针对消息的“标签”。我没有真正遵循@Andy 的方法,而是对“匹配”列进行了重复删除,然后在前面的答案中成功地从 Jeff 那里删除了。
    【解决方案2】:

    先重置索引,然后使用你提到的解决方案:

    In [28]: s
    Out[28]:
    Date
    2013-08-06 16:03:17          []
    2013-08-06 16:03:12          []
    2013-08-06 16:03:10          []
    2013-08-06 16:03:09          []
    2013-08-06 16:03:08          []
    2013-08-06 16:03:07          []
    2013-08-06 16:03:07    [abbott]
    2013-08-06 16:03:06          []
    2013-08-06 16:03:02          []
    2013-08-06 16:03:00      [rudd]
    Name: matches, dtype: object
    
    In [29]: df = s.reset_index()
    
    In [30]: df.join(df.matches.apply(lambda x: Series(1, index=x)).fillna(0))
    Out[30]:
                     Date   matches  abbott  rudd
    0 2013-08-06 16:03:17        []       0     0
    1 2013-08-06 16:03:12        []       0     0
    2 2013-08-06 16:03:10        []       0     0
    3 2013-08-06 16:03:09        []       0     0
    4 2013-08-06 16:03:08        []       0     0
    5 2013-08-06 16:03:07        []       0     0
    6 2013-08-06 16:03:07  [abbott]       1     0
    7 2013-08-06 16:03:06        []       0     0
    8 2013-08-06 16:03:02        []       0     0
    9 2013-08-06 16:03:00    [rudd]       0     1
    

    除非您对 DatetimeIndex 有明确的用例(通常涉及某种类型的重新采样,并且没有重复),否则最好将日期放入列中,因为它比将其保留为索引更灵活,特别是如果所述索引有重复。

    就apply 方法而言,它对不同的对象做的事情略有不同。例如,DataFrame.apply() 将默认将传入的 callable 应用于列,但您可以传递 axis=1 以将其应用于行。

    Series.apply() 将传入的可调用对象应用于Series 实例的每个元素。对于@Jeff 提供的非常聪明的解决方案,发生的情况如下:

    In [12]: s
    Out[12]:
    Date
    2013-08-06 16:03:17          []
    2013-08-06 16:03:12          []
    2013-08-06 16:03:10          []
    2013-08-06 16:03:09          []
    2013-08-06 16:03:08          []
    2013-08-06 16:03:07          []
    2013-08-06 16:03:07    [abbott]
    2013-08-06 16:03:06          []
    2013-08-06 16:03:02          []
    2013-08-06 16:03:00      [rudd]
    Name: matches, dtype: object
    
    In [13]: pd.lib.map_infer(s.values, lambda x: Series(1, index=x)).tolist()
    Out[13]:
    [Series([], dtype: int64),
     Series([], dtype: int64),
     Series([], dtype: int64),
     Series([], dtype: int64),
     Series([], dtype: int64),
     Series([], dtype: int64),
     abbott    1
    dtype: int64,
     Series([], dtype: int64),
     Series([], dtype: int64),
     rudd    1
    dtype: int64]
    
    In [14]: pd.core.frame._to_arrays(_13, columns=None)
    Out[14]:
    (array([[ nan,  nan,  nan,  nan,  nan,  nan,   1.,  nan,  nan,  nan],
           [ nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,   1.]]),
     Index([u'abbott', u'rudd'], dtype=object))
    

    Out[13] 中的每个空 Series 都被赋予一个值 nan,以表明我们的任一列索引都没有值。在本例中,该索引为Index([u'abbott', u'rudd'], dtype=object)。 列索引处的值将被保留。

    请记住,这些是用户通常不必担心的低级细节。我很好奇,所以我跟着代码的踪迹。

    【讨论】:

    • 我不认为异常的原因是因为 DatetimeIndex 中的重复,而是在 Series(1, index=x) 的 x 中(我可以错了)....
    • @AndyHayden 你是对的。事实上,我无法使用 OP 的数据重现异常。
    • 您可以使用 findall 中的欺骗复制它(请参阅我的回答),我认为 Jeff 的应用解决方案应该适用于 DatetimeIndex 中的欺骗。
    • @AndyHayden 当您的数据中有 ['rudd', 'rudd'] 之类的内容时,它会失败(根据您的示例)。这是因为您无法为骗子index 创建索引器。试试i = Index(['a', 'a']); i.get_indexer(i)
    • 哦,所以这更像是对 Jeff 机智回答的阐述。 (我很困惑!)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多