【问题标题】:How to include strings with Pandas resample如何使用 Pandas 重新采样包含字符串
【发布时间】:2015-01-16 14:44:14
【问题描述】:

我有一个数据系列,其中包含一个随机日期列作为我的索引、一个编号值以及三列,每列都指示是否激活了安全机制来阻止编号值。例子是:

DateTime         Safe1    Safe2    Safe3    Measurement

1/8/2013 6:06     N       Y        N    

1/8/2013 6:23     N       Y        N    

1/8/2013 6:40     N       N        N        28

1/8/2013 6:57     N        N       N        31

我需要使用 Pandas 对数据进行重新采样,以创建干净的半小时间隔数据,并取任何存在的值的平均值。当然,这会删除三个安全字符串列。

但是,如果在整个半小时间隔内激活了任何安全机制组合,我想包含一个指示 Y 的列。

如何获得此字符串列,在重新采样的数据中显示 Y,表明 Y 存在于三个安全机制列中的原始数据中,而测量中没有任何值?

基于上述的期望输出:

DateTime      Safe1  Measurement

1/8/2013 6:00 Y      

1/8/2013 6:30 N      29.5

【问题讨论】:

    标签: python string pandas resampling


    【解决方案1】:

    我认为使用 resample 功能不可能做你想做的事,因为你可以做的自定义不多。我们必须用 groupby 操作来做一个 TimeGrouper。

    首先创建数据:

    import pandas as pd
    
    index = ['1/8/2013 6:06', '1/8/2013 6:23', '1/8/2013 6:40', '1/8/2013 6:57']
    
    data = {'Safe1' : ['N', 'N', 'N', 'N'], 
            'Safe2': ['Y', 'Y', 'N', 'N'], 
            'Safe3': ['N', 'N', 'N', 'N'], 
            'Measurement': [0,0,28,31]}
    
    df = pd.DataFrame(index=index, data=data)
    df.index = pd.to_datetime(df.index)
    df
    

    输出:

                         Measurement Safe1 Safe2 Safe3
    2013-01-08 06:06:00            0     N     Y     N
    2013-01-08 06:23:00            0     N     Y     N
    2013-01-08 06:40:00           28     N     N     N
    2013-01-08 06:57:00           31     N     N     N
    

    然后让我们添加一个名为 Safe 的辅助列,它将是所有 Safex 列的串联。如果 Safe 列中至少有一个 Y,我们就会知道安全机制已被激活。

    df['Safe'] = df['Safe1'] + df['Safe2'] + df['Safe3']
    print df
    

    输出:

                         Measurement Safe1 Safe2 Safe3 Safe
    2013-01-08 06:06:00            0     N     Y     N  NYN
    2013-01-08 06:23:00            0     N     Y     N  NYN
    2013-01-08 06:40:00           28     N     N     N  NNN
    2013-01-08 06:57:00           31     N     N     N  NNN
    

    最后,我们将定义一个自定义函数,如果作为参数传递的字符串列表中至少有一个 Y,它将返回 Y .

    在我们按 30 分钟间隔对其进行分组后,该自定义函数将传递到 Safe 列:

    def func(x):
        x = ''.join(x.values)
        return 'Y' if 'Y' in x else 'N'
    
    df.groupby(pd.TimeGrouper(freq='30Min')).agg({'Measurement': 'mean', 'Safe': func })
    

    输出:

                        Safe  Measurement
    2013-01-08 06:00:00    Y          0.0
    2013-01-08 06:30:00    N         29.5
    

    【讨论】:

    • pd.TimeGrouper 已重命名为 pd.Grouper... 在我的情况下,列 agg-column 名称必须事先存在(比较 stackoverflow.com/questions/60229375/…)。除此之外,它仍然像一个魅力:)
    【解决方案2】:

    这是使用 pandas 内置重采样功能的答案。

    首先将 3 个 Safe 值合并为一列:

    df['Safe'] = df.Safe1 + df.Safe2 + df.Safe3
    

    将3个字母的字符串变成0-1的变量:

    df.Safe = df.Safe.apply(lambda x: 1 if 'Y' in x else 0)
    

    为“保险箱”列编写自定义重采样函数:

    def f(x):
      if sum(x) > 0: return 'Y'
      else: return 'N'
    

    最后,重新采样:

    df.resample('30T').Safe.agg({'Safe': f}).join(df.resample('30T').Measurement.mean())
    

    输出:

                        Safe  Measurement 
    2013-01-08 06:00:00  Y        0.0
    2013-01-08 06:30:00  N        29.5
    

    【讨论】:

      【解决方案3】:

      我手动重新采样日期(如果是四舍五入很容易)....

      这是一个例子

      from random import shuffle
      from datetime import datetime, timedelta
      from itertools import zip_longest
      from random import randint, randrange, seed
      from tabulate import tabulate
      import pandas as pd
      
      def df_to_md(df):
          print(tabulate(df, tablefmt="pipe",headers="keys"))
      
      seed(42)
      
      people=['tom','dick','harry']
      avg_score=[90,50,10]
      date_times=[n for n in pd.date_range(datetime.now()-timedelta(days=2),datetime.now(),freq='5 min').values]
      scale=1+int(len(date_times)/len(people))
      score =[randint(i,100)*i/10000 for i in avg_score*scale]
      
      df=pd.DataFrame.from_records(list(zip(date_times,people*scale,score)),columns=['When','Who','Status'])
      # Create 3 records tom should score 90%, dick 50% and poor harry only 10% 
      # Tom should score well
      df_to_md(df[df.Who=='tom'].head())
      

      表格是 Markdown 格式 - 只是为了方便我的剪切和粘贴....

      |    | When                       | Who   |   Status |
      |---:|:---------------------------|:------|---------:|
      |  0 | 2019-06-18 14:07:17.457124 | tom   |    0.9   |
      |  3 | 2019-06-18 14:22:17.457124 | tom   |    0.846 |
      |  6 | 2019-06-18 14:37:17.457124 | tom   |    0.828 |
      |  9 | 2019-06-18 14:52:17.457124 | tom   |    0.9   |
      | 12 | 2019-06-18 15:07:17.457124 | tom   |    0.819 |
      

      哈利成绩不好

      df_to_md(df[df.Who=='harry'].head())
      
      |    | When                       | Who   |   Status |
      |---:|:---------------------------|:------|---------:|
      |  2 | 2019-06-18 14:17:17.457124 | harry |    0.013 |
      |  5 | 2019-06-18 14:32:17.457124 | harry |    0.038 |
      |  8 | 2019-06-18 14:47:17.457124 | harry |    0.023 |
      | 11 | 2019-06-18 15:02:17.457124 | harry |    0.079 |
      | 14 | 2019-06-18 15:17:17.457124 | harry |    0.064 |
      

      让我们得到每人每小时的平均值

      def round_to_hour(t):
          # Rounds to nearest hour by adding a timedelta hour if minute >= 30
          return (t.replace(second=0, microsecond=0, minute=0, hour=t.hour)
                     +timedelta(hours=t.minute//30))
      

      并使用此方法生成一个新列。

      df['WhenRounded']=df.When.apply(lambda x: round_to_hour(x))
      df_to_md(df[df.Who=='tom'].head())
      
      

      这应该是汤姆的数据 - 显示原始和四舍五入。

      |    | When                       | Who   |   Status | WhenRounded         |
      |---:|:---------------------------|:------|---------:|:--------------------|
      |  0 | 2019-06-18 14:07:17.457124 | tom   |    0.9   | 2019-06-18 14:00:00 |
      |  3 | 2019-06-18 14:22:17.457124 | tom   |    0.846 | 2019-06-18 14:00:00 |
      |  6 | 2019-06-18 14:37:17.457124 | tom   |    0.828 | 2019-06-18 15:00:00 |
      |  9 | 2019-06-18 14:52:17.457124 | tom   |    0.9   | 2019-06-18 15:00:00 |
      | 12 | 2019-06-18 15:07:17.457124 | tom   |    0.819 | 2019-06-18 15:00:00 |
      

      我们可以重采样 ... 通过分组和使用分组函数

      按舍入日期和 Person(Datetime 和 Str)对象分组)- 在这种情况下,我们想要 mean 值,但还有其他可用的值。

      
      df_resampled=df.groupby(by=['WhenRounded','Who'], axis=0).agg({'Status':'mean'}).reset_index()
      # Output in Markdown format
      df_to_md(df_resampled[df_resampled.Who=='tom'].head())
      
      |    | WhenRounded         | Who   |   Status |
      |---:|:--------------------|:------|---------:|
      |  2 | 2019-06-18 14:00:00 | tom   |  0.873   |
      |  5 | 2019-06-18 15:00:00 | tom   |  0.83925 |
      |  8 | 2019-06-18 16:00:00 | tom   |  0.86175 |
      | 11 | 2019-06-18 17:00:00 | tom   |  0.84375 |
      | 14 | 2019-06-18 18:00:00 | tom   |  0.8505  |
      

      让我们检查一下汤姆 @ 14:00 的平均值

      print("Check tom 14:00 .86850  ... {:6.5f}".format((.900+.846+.828+.900)/4))
      
      Check tom 14:00 .86850  ... 0.86850
      

      希望对你有所帮助

      【讨论】:

        猜你喜欢
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-20
        • 2019-04-15
        • 2017-11-26
        • 2013-05-24
        • 2013-06-09
        相关资源
        最近更新 更多