[英]Using SciPy curve_fit to predict post final score
我有一个帖子,我需要尽可能接近地预测最终得分。
显然使用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
一个示例数据点看起来像这样:
[n_votes, n_comments, hour]
这是我尝试的一团糟,结果看起来并不正确。
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()
该图应显示初始/最终得分和当前预测。
我对这个功能也有一些疑问。最初这是它的样子:
(votes_per_minute + n_comments) * 60 * hour
但是我用投票取代了votes_per_minute
。 考虑到我在2分钟后收集这些数据,并且我有一个参数,我会说它不是太糟糕但我真的不知道。
再次,谁保证这是最好的功能? 能够自动发现这个功能会很好,但我认为这是ML领域......
编辑:
关于测量:我可以获得尽可能多的(每15-30-60个),尽管它们必须在帖子<= 3分钟时收集。
免责声明:这只是关于如何解决此问题的建议。 可能有更好的选择。
我认为,考虑到elapsed-time-since-posting
与final-score
之间的关系可能会有所帮助。 来自[OC] Upvotes的以下曲线随着时间的推移,reddit帖子模拟了final-score
或total-upvotes-count
final-score
total-upvotes-count
的行为及时:
曲线显然依赖于这样一个事实,即一旦一个帖子在线,你会期望一些线性上升的upvotes行为慢慢收敛/稳定在一个最大值(并从那里你有一个平缓/平缓的斜率)。
此外,我们知道通常票数/评论的数量随着时间的推移而递增。 这些元素之间的关系可以被认为是一个系列,我选择将其建模为几何级数(如果你看到它更好,你可以考虑算术一个)。 此外,你必须记住,你在计算一些元素两次; 一些用户评论和投票,所以你计算了两次,也有些人可以多次评论,但只能投票一次。 我选择考虑只有70%(代码p = 0.7
)的用户是独特的评论者,评论和投票的用户代表60%(代码e = 1-0.6 = 0.4
)的用户总数(评论者和赞成者),这些假设的结果:
因此,我们有两个方程来对分数进行建模,以便您可以将它们组合起来并取其平均值。 在代码中,这将是这样的:
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()
上一个代码的输出如下: 显然,提供的数据集太小,无法评估任何方法,因此您需要对此进行更多测试。
这里的主要想法是你假设你的数据遵循某个函数/行为(在func
描述),但你给它一定的自由度(你的参数: a
, d
, q
),并使用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))
输出:
score for init-votes = 2 & init-comments = 0 is 14.000150386210994
您可以看到此输出接近正确值13
并且希望通过更多数据,您可以获得更好/更准确的参数近似值,从而获得更好的“预测”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.