【问题标题】:Iterate pandas data frame for rows consists of arrays and compute a moving average based on condition迭代pandas数据框的行由数组组成并根据条件计算移动平均值
【发布时间】:2019-02-23 09:19:19
【问题描述】:

我想不出我要解决的问题。 我有一个来自这个的熊猫数据框:

date,       id,     measure,    result
2016-07-11, 31, "[2, 5, 3, 3]",     1
2016-07-12, 32, "[3, 5, 3, 3]",     1
2016-07-13, 33, "[2, 1, 2, 2]",     1
2016-07-14, 34, "[2, 6, 3, 3]",     1
2016-07-15, 35, "[39, 31, 73, 34]", 0
2016-07-16, 36, "[3, 2, 3, 3]",     1
2016-07-17, 37, "[3, 8, 3, 3]",     1

Measurements 列由字符串格式的数组组成。

我想从过去 3 个 测量记录中获得一个 moving-average-array 列,不包括那些result 为 0 的记录。过去 3 个记录意味着对于id 34,要使用id 31,32,33 的数组。

这是关于取每个第 1 点、第 2 点、第 3 和第 4 点的平均值来得到这个moving-average-array

不是要获取第一个数组、第二个数组的平均值……然后取平均值,no

对于前3行,因为没有足够的历史,我只想用自己的测量。所以解决方案应该是这样的:

date,       id,     measure,    result .     Solution
2016-07-11, 31, "[2, 5, 3, 3]",     1,      "[2,   5, 3,   3]"
2016-07-12, 32, "[3, 5, 3, 3]",     1,      "[3,   5, 3,   3]"
2016-07-13, 33, "[2, 1, 2, 2]",     1,      "[2,   1, 2,   2]"
2016-07-14, 34, "[2, 6, 3, 3]",     1,      "[2.3, 3.6, 2.6, 2.6]"
2016-07-15, 35, "[39, 31, 73, 34]", 0,      "[2.3, 4, 2.6, 2.6]"
2016-07-16, 36, "[3, 2, 3, 3]",     1,      "[2.3, 4, 2.6, 2.6]"
2016-07-17, 37, "[3, 8, 3, 3]",     1,      "[2.3, 3, 2.6, 2.6]"

真实数据更大。 result 0 也可以在彼此之后重复 2 次或更多次。我认为这将是关于跟踪以前的 OK results 正确获得这些平均值。我花了时间,但我做不到。

我在这里发布数据框:

 mydict = {'date': {0: '2016-07-11',
      1: '2016-07-12',
      2: '2016-07-13',
      3: '2016-07-14',
      4: '2016-07-15',
      5: '2016-07-16',
      6: '2016-07-17'},
     'id': {0: 31, 1: 32, 2: 33, 3: 34, 4: 35, 5: 36, 6: 37},
     'measure': {0: '[2, 5, 3, 3]',
      1: '[3, 5, 3, 3]',
      2: '[2, 1, 2, 2]',
      3: '[2, 6, 3, 3]',
      4: '[39, 31, 73, 34]',
      5: '[3, 2, 3, 3]',
      6: '[3, 8, 3, 3]'},
     'result': {0: 1, 1: 1, 2: 1, 3: 1, 4: 0, 5: 1, 6: 1}}

df = pd.DataFrame(mydict)

感谢您提供指示或指出如何操作。

【问题讨论】:

  • 你的度量是str还是int列表,双引号表示str?并且您可以删除结果为 0 的行?
  • 它是一个字符串,您可以解析并生成一个数字列表。最初,它们是像 3.34、2.45 这样的浮点数。为了简单起见,我在那里输入了整数。我们不允许删除结果为 0 的行,我们需要它们。

标签: python pandas


【解决方案1】:

仅使用 1 个 for 循环的解决方案:

考虑数据:

mydict = {'date': {0: '2016-07-11',
      1: '2016-07-12',
      2: '2016-07-13',
      3: '2016-07-14',
      4: '2016-07-15',
      5: '2016-07-16',
      6: '2016-07-17'},
     'id': {0: 31, 1: 32, 2: 33, 3: 34, 4: 35, 5: 36, 6: 37},
     'measure': {0: '[2, 5, 3, 3]',
      1: '[3, 5, 3, 3]',
      2: '[2, 1, 2, 2]',
      3: '[2, 6, 3, 3]',
      4: '[39, 31, 73, 34]',
      5: '[3, 2, 3, 3]',
      6: '[3, 8, 3, 3]'},
     'result': {0: 1, 1: 1, 2: 1, 3: 1, 4: 0, 5: 1, 6: 1}}
df = pd.DataFrame(mydict)

我定义了一个简单的函数来计算均值并返回一个列表。然后,循环应用规则的数据框:

def calc_mean(in_list):
    p0 = round((in_list[0][0] + in_list[1][0] + in_list[2][0])/3,1)
    p1 = round((in_list[0][1] + in_list[1][1] + in_list[2][1])/3,1)
    p2 = round((in_list[0][2] + in_list[1][2] + in_list[2][2])/3,1)
    p3 = round((in_list[0][3] + in_list[1][3] + in_list[2][3])/3,1)
    return [p0, p1, p2, p3]

Solution = []
aux_list = []
for index, row in df.iterrows():
    if index in [0,1,2]:
        Solution.append(row.measure)
        aux_list.append([int(x) for x in row.measure[1:-1].split(', ')])
    else:
        Solution.append('[' +', '.join(map(str, calc_mean(aux_list))) + ']')
        if row.result > 0:
            aux_list.pop(0)
            aux_list.append([int(x) for x in row.measure[1:-1].split(', ')])        
df['Solution'] = Solution

输出是:

请注意,结果四舍五入到小数点后 1 位,与您想要的输出略有不同。对我来说更有意义。

编辑:

作为 @Frenchy 在 cmets 中的建议,为了处理前 3 行中的 result == 0,我们需要稍微更改第一个 if 子句:

if index in [0,1,2] or len(aux_list) <3:
    Solution.append(row.measure)
    if row.result > 0:
        aux_list.append([int(x) for x in row.measure[1:-1].split(', ')])

【讨论】:

  • 谢谢丹尼尔。这真的很有帮助。事实上,我的数据很大而且很脏。但你的帮助为我扫清了道路。
  • 嗨@Silvana,很高兴知道它很有帮助。如果您认为我的回答值得,请您投票并接受我的回答吗?
  • 嗨@daniel。我投了赞成票,但网站说它不可见,因为我没有任何声誉。为了获得声誉,我需要他们说的一些赞成票。那么,如果您认为这个问题对社区有用,您可以投票支持这个问题吗?
  • 谢谢。但我认为即使没有声誉,您也可以接受答案...
  • 我真的看不到像accept the answer 这样的东西。如果网站允许,我会继续检查并accept。谢谢。
【解决方案2】:

您可以使用pd.evalliststr 更改为正确的list,仅measure 中的部分数据,其中result 不为0。使用rolling 和@ 987654333@ 然后shift 在下一行获得最后 3 行的滚动平均值。然后将map 更改为str,一旦您的数据框更改为带有valuestolist 的列表列表。最后只需要替换掉前三行和ffill缺失的数据即可:

df.loc[df.result.shift() != 0,'solution'] = list(map(str,
                              pd.DataFrame(pd.eval(df[df.result != 0].measure))
                                .rolling(3).mean().shift().values.tolist()))
df.loc[:2,'solution'] = df.loc[:2,'measure']
df.solution = df.solution.ffill()

【讨论】:

  • 嗨@Ben.T。谢谢你。这真的帮助了我很多。你的方法很干净,效果很好。
  • 我对您的解决方案的唯一问题是与此条目相关的原因。 eval 无法处理我的大数据:stackoverflow.com/questions/48008191/…
  • @Silvana 感谢您指出这一点。我想然后使用您提供的链接的解决方案之一来替换 eval 可以为更大的数据做到这一点:)
【解决方案3】:

这是另一个解决方案:

# get data to reproduce example
from io import StringIO
data = StringIO(""" 
    date;id;measure;result 
    2016-07-11;31;"[2,5,3,3]";1 
    2016-07-12;32;"[3,5,3,3]";1 
    2016-07-13;33;"[2,1,2,2]";1 
    2016-07-14;34;"[2,6,3,3]";1 
    2016-07-15;35;"[39,31,73,34]";0 
    2016-07-16;36;"[3,2,3,3]";1 
    2016-07-17;37;"[3,8,3,3]";1 
    """)  

df = pd.read_csv(data, sep=";")
df
# Out:
#          date  id        measure  result
# 0  2016-07-11  31      [2,5,3,3]       1
# 1  2016-07-12  32      [3,5,3,3]       1
# 2  2016-07-13  33      [2,1,2,2]       1
# 3  2016-07-14  34      [2,6,3,3]       1
# 4  2016-07-15  35  [39,31,73,34]       0
# 5  2016-07-16  36      [3,2,3,3]       1
# 6  2016-07-17  37      [3,8,3,3]       1  

# convert values in measure column to lists
from ast import literal_eval
dm = df['measure'].apply(literal_eval)

# apply rolling mean with period 2 and recollect values into list in column means
df["means"] = dm.apply(pd.Series).rolling(2, min_periods=0).mean().values.tolist()                            

df                                                                                                           
# Out: 
#          date  id        measure  result                     means
# 0  2016-07-11  31      [2,5,3,3]       1      [2.0, 5.0, 3.0, 3.0]
# 1  2016-07-12  32      [3,5,3,3]       1      [2.5, 5.0, 3.0, 3.0]
# 2  2016-07-13  33      [2,1,2,2]       1      [2.5, 3.0, 2.5, 2.5]
# 3  2016-07-14  34      [2,6,3,3]       1      [2.0, 3.5, 2.5, 2.5]
# 4  2016-07-15  35  [39,31,73,34]       0  [20.5, 18.5, 38.0, 18.5]
# 5  2016-07-16  36      [3,2,3,3]       1  [21.0, 16.5, 38.0, 18.5]
# 6  2016-07-17  37      [3,8,3,3]       1      [3.0, 5.0, 3.0, 3.0]

# moving window of size 3
df["means"] = dm.apply(pd.Series).rolling(3, min_periods=0).mean().round(2).values.tolist()
df
# Out: 
#             date  id        measure  result                        means
# 0  2016-07-11  31      [2,5,3,3]       1         [2.0, 5.0, 3.0, 3.0]
# 1  2016-07-12  32      [3,5,3,3]       1         [2.5, 5.0, 3.0, 3.0]
# 2  2016-07-13  33      [2,1,2,2]       1     [2.33, 3.67, 2.67, 2.67]
# 3  2016-07-14  34      [2,6,3,3]       1      [2.33, 4.0, 2.67, 2.67]
# 4  2016-07-15  35  [39,31,73,34]       0   [14.33, 12.67, 26.0, 13.0]
# 5  2016-07-16  36      [3,2,3,3]       1  [14.67, 13.0, 26.33, 13.33]
# 6  2016-07-17  37      [3,8,3,3]       1  [15.0, 13.67, 26.33, 13.33]    

【讨论】:

  • 感谢您的回复。但是,您将 [39,31,73,34] 之类的大测量数字包含在计算中以求平均值。这就是你的解决方案的问题。看看你的平均值有多大。这是因为你包括了不好的结果。我希望这可以说清楚。谢谢。
猜你喜欢
  • 2014-04-01
  • 2017-07-07
  • 2018-06-22
  • 1970-01-01
  • 1970-01-01
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 2017-02-16
相关资源
最近更新 更多