使用SciPy curve_fit预测最终得分

A. *_*ion 6 python curve-fitting scipy

我有一个帖子,我需要尽可能地预测最终分数。

显然使用curve_fit应该可以解决问题,尽管我并不是很了解如何使用它。

我有两个已知的值,它们是在帖子发布2分钟后收集的。

这些是评论计数(称为n_comments)和投票计数(称为)n_votes

一个小时后,我再次检查该帖子,并获得final_score(所有投票的总和)值,这是我想要预测的值。

我在网上查看了不同的示例,但是它们都使用多个数据点(我只有2个),而且,我的初始数据点包含更多信息(n_votes和n_comments),因为我发现没有其他数据则无法准确预测分数。

要使用curve_fit您需要一个功能。我的看起来像这样:

def func(datapoint,k,t,s):
    return ((datapoint[0]*k+datapoint[1]*t)*60*datapoint[2])*s
Run Code Online (Sandbox Code Playgroud)

样本数据点如下所示:

[n_votes, n_comments, hour] 
Run Code Online (Sandbox Code Playgroud)

这是我的尝试的零星混乱,结果看起来根本不正确。

 import numpy as np
 import matplotlib.pyplot as plt
 from scipy.optimize import curve_fit


 initial_votes_list = [3, 1, 2, 1, 0]
 initial_comment_list = [0, 3, 0, 1, 64]
 final_score_list = [26,12,13,14,229]

 # Those lists contain data about multiple posts; I want to predict one at a time, passing the parameters to the next.

 def func(x,k,t,s):
     return ((x[0]*k+x[1]*t)*60*x[2])*s

 x = np.array([3, 0, 1])
 y = np.array([26 ,0 ,2])
 #X = [[a,b,c] for a,b,c in zip(i_votes_list,i_comment_list,[i for i in range(len(i_votes_list))])]


 popt, pcov = curve_fit(func, x, y)

 plt.plot(x, [ 1 , func(x, *popt), 2], 'g--',
          label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt))

 plt.xlabel('Time')
 plt.ylabel('Score')
 plt.legend()
 plt.show()
Run Code Online (Sandbox Code Playgroud)

该图显示初始/最终分数和当前预测。

我对功能也有一些怀疑。最初的样子是这样的:

(votes_per_minute + n_comments) * 60 * hour
Run Code Online (Sandbox Code Playgroud)

但是我votes_per_minute只投了赞成票。考虑到我收集这些数据的2分钟后,和我有一个参数有,我会说,这不是糟糕,但我真的不知道。

再次,谁保证这是可能的最佳功能?自动发现该功能会很好,但是我认为这是ML领域...

编辑:

关于测量:我可以得到我想要的数量(每15-30-60秒),尽管必须在帖子的发布时间小于3分钟时收集它们。

Sup*_*ito 5

免责声明:这只是关于您如何解决此问题的建议。可能有更好的选择。

我想,这可能是有帮助的考虑之间的关系elapsed-time-since-postingfinal-score。以下来自[OC] Upvotes 的 Reddit 帖子随时间推移的曲线对final-score或的行为进行了建模total-upvotes-count在此处输入图片说明

该曲线显然依赖于这样一个事实,即一旦帖子在线,您就会期待一些线性上升的赞成行为,该行为会慢慢收敛/稳定在最大值附近(并且从那里您有一个平缓/平坦的斜率)。

此外,我们知道通常投票/评论的数量随着时间的推移而上升。这些元素之间的关系可以认为是一个系列,我选择将其建模为几何级数(如果您看到更好,可以考虑算术1)。此外,您必须记住,您对某些元素进行了两次计数;有些用户评论和点赞,所以你数了两次,也有些用户可以评论多次但只能点赞一次。我选择考虑只有 70%(在代码中p = 0.7)的用户是唯一的评论者,并且评论和点赞e = 1-0.6 = 0.4的用户占用户总数(评论者和点赞者)的60%(在代码中),这些假设的结果:

在此处输入图片说明

所以我们有两个方程来模拟分数,这样你就可以将它们组合起来并取它们的平均值。在代码中,这看起来像这样:

import warnings 
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from mpl_toolkits.mplot3d import axes3d
# filter warnings
warnings.filterwarnings("ignore")

class Cfit: 
    def __init__(self, votes, comments, scores, fit_size):
        self.votes    = votes
        self.comments = comments
        self.scores   = scores
        self.time     = 60          # prediction time 
        self.fit_size = fit_size
        self.popt     = []

    def func(self, x, a, d, q):
        e = 0.4
        b = 1
        p = 0.7
        return (a * np.exp( 1-(b / self.time**d )) + q**self.time * e * (x + p*self.comments[:len(x)]) ) /2

    def fit_then_predict(self):
        popt, pcov = curve_fit(self.func, self.votes[:self.fit_size], self.scores[:self.fit_size])
        return popt, pcov


# init
init_votes    = np.array([3,   1,  2,  1,   0])
init_comments = np.array([0,   3,  0,  1,  64])
final_scores  = np.array([26, 12, 13, 14, 229])

# fit and predict
cfit       = Cfit(init_votes, init_comments, final_scores, 15)
popt, pcov = cfit.fit_then_predict()

# plot expectations
fig = plt.figure(figsize = (15,15))
ax1 = fig.add_subplot(2,3,(1,3), projection='3d')
ax1.scatter(init_votes, init_comments, final_scores,                 'go',  label='expected')
ax1.scatter(init_votes, init_comments, cfit.func(init_votes, *popt), 'ro', label = 'predicted')
# axis
ax1.set_xlabel('init votes count')
ax1.set_ylabel('init comments count')
ax1.set_zlabel('final score')
ax1.set_title('fincal score = f(init votes count, init comments count)')

plt.legend()

# evaluation: diff = expected - prediction
diff = abs(final_scores - cfit.func(init_votes, *popt))
ax2  = fig.add_subplot(2,3,4)
ax2.plot(init_votes, diff, 'ro', label='fit: a=%5.3f, d=%5.3f, q=%5.3f' % tuple(popt))
ax2.grid('on')
ax2.set_xlabel('init votes count')
ax2.set_ylabel('|expected-predicted|')
ax2.set_title('|expected-predicted| = f(init votes count)')


# plot expected and predictions as f(init-votes)
ax3  = fig.add_subplot(2,3,5)
ax3.plot(init_votes, final_scores, 'gx', label='fit: a=%5.3f, d=%5.3f, q=%5.3f' % tuple(popt))
ax3.plot(init_votes, cfit.func(init_votes, *popt), 'rx', label='fit: a=%5.3f, d=%5.3f, q=%5.3f' % tuple(popt))
ax3.set_xlabel('init votes count')
ax3.set_ylabel('final score')
ax3.set_title('fincal score = f(init votes count)')
ax3.grid('on')

# plot expected and predictions as f(init-comments)
ax4  = fig.add_subplot(2,3,6)
ax4.plot(init_votes, final_scores, 'gx', label='fit: a=%5.3f, d=%5.3f, q=%5.3f' % tuple(popt))
ax4.plot(init_votes, cfit.func(init_votes, *popt), 'rx', label='fit: a=%5.3f, d=%5.3f, q=%5.3f' % tuple(popt))
ax4.set_xlabel('init comments count')
ax4.set_ylabel('final score')
ax4.set_title('fincal score = f(init comments count)')
ax4.grid('on')
plt.show()
Run Code Online (Sandbox Code Playgroud)

前面代码的输出如下: 在此处输入图片说明 很明显,提供的数据集太小,无法评估任何方法,因此您需要对此进行更多测试。

这里的主要想法是,你认为你的数据按照一定的功能/行为(描述func),但你给它一定的自由度(你的参数:adq),并使用curve_fit你试图接近这些变量的最佳组合将使您的输入数据适合您的输出数据。从curve_fit(在代码中popt)获得返回的参数后,您只需使用这些参数运行您的函数,例如(在上一个代码的末尾添加此部分):

# a function similar to func to predict scores for a certain values
def score(votes_count, comments_count, popt):
    e, b, p = 0.4, 1, 0.7
    a, d, q = popt[0], popt[1], popt[2]
    t       = 60
    return (a * np.exp( 1-(b / t**d )) + q**t * e * (votes_count + p*comments_count )) /2

print("score for init-votes = 2 & init-comments = 0 is ", score(2, 0, popt))
Run Code Online (Sandbox Code Playgroud)

输出:

score for init-votes = 2 & init-comments = 0 is 14.000150386210994
Run Code Online (Sandbox Code Playgroud)

您可以看到此输出接近正确值13,希望通过更多数据,您可以获得更好/更准确的参数近似值,从而获得更好的“预测”。