python 2.7 中对数对数刻度的最佳拟合线
Best Fit Line on Log Log Scales in python 2.7
这是一个以对数刻度表示的网络 IP 频率排名图。完成这部分后,我尝试使用 Python 2.7 在对数对数刻度上绘制最佳拟合线。我必须使用 matplotlib 的 "symlog" 轴刻度,否则某些值无法正确显示并且某些值会被隐藏。
我正在绘制的数据的 X 值是 URLs,Y 值是 URLs 的对应频率。
我的数据如下所示:
'http://www.bing.com/search?q=d2l&src=IE-TopResult&FORM=IETR02&conversationid= 123 0.00052210688591'
`http://library.uc.ca/ 118 4.57782298326e-05`
`http://www.bing.com/search?q=d2l+uofc&src=IE-TopResult&FORM=IETR02&conversationid= 114 4.30271029472e-06`
`http://www.nature.com/scitable/topicpage/genetics-and-statistical-analysis-34592 109 1.9483268261e-06`
数据包含第一列中的 URL,第二列中包含相应的频率(相同 URL 出现的次数),最后是第三列中传输的字节。首先,我仅使用第一列和第二列进行此分析。总共有 2,465 个 x 值或唯一 URLs.
以下是我的代码
import os
import matplotlib.pyplot as plt
import numpy as np
import math
from numpy import *
import scipy
from scipy.interpolate import *
from scipy.stats import linregress
from scipy.optimize import curve_fit
file = open(filename1, 'r')
lines = file.readlines()
result = {}
x=[]
y=[]
for line in lines:
course,count,size = line.lstrip().rstrip('\n').split('\t')
if course not in result:
result[course] = int(count)
else:
result[course] += int(count)
file.close()
frequency = sorted(result.items(), key = lambda i: i[1], reverse= True)
x=[]
y=[]
i=0
for element in frequency:
x.append(element[0])
y.append(element[1])
z=[]
fig=plt.figure()
ax = fig.add_subplot(111)
z=np.arange(len(x))
print z
logA = [x*np.log(x) if x>=1 else 1 for x in z]
logB = np.log(y)
plt.plot(z, y, color = 'r')
plt.plot(z, np.poly1d(np.polyfit(logA, logB, 1))(z))
ax.set_yscale('symlog')
ax.set_xscale('symlog')
slope, intercept = np.polyfit(logA, logB, 1)
plt.xlabel("Pre_referer")
plt.ylabel("Popularity")
ax.set_title('Pre Referral URL Popularity distribution')
plt.show()
您会看到导入了很多库,因为我已经使用了很多库,但是 none 我的实验产生了预期的结果。所以上面的代码正确地生成了排名图。可以看出,这是红线,但曲线中的蓝线本应是最佳拟合线,但在视觉上是不正确的。这是生成的图表。
这是我期待的图表。第二张图中的虚线是我以某种方式错误绘制的。
关于如何解决这个问题有什么想法吗?
在双对数尺度上沿直线落下的数据遵循 y = c*x^(m)
形式的幂关系。两边取对数,得到拟合的线性方程:
log(y) = m*log(x) + c
调用 np.polyfit(log(x), log(y), 1)
提供 m
和 c
的值。然后,您可以使用这些值来计算 log_y_fit
的拟合值:
log_y_fit = m*log(x) + c
您想要根据原始数据绘制的拟合值是:
y_fit = exp(log_y_fit) = exp(m*log(x) + c)
那么,您遇到的两个问题是:
您正在使用原始 x 坐标计算拟合值,而不是 log(x) 坐标
您正在绘制拟合 y 值的对数而不将它们转换回原始比例
我在下面的代码中通过将 plt.plot(z, np.poly1d(np.polyfit(logA, logB, 1))(z))
替换为:
解决了这两个问题
m, c = np.polyfit(logA, logB, 1) # fit log(y) = m*log(x) + c
y_fit = np.exp(m*logA + c) # calculate the fitted values of y
plt.plot(z, y_fit, ':')
这可以放在一行中:plt.plot(z, np.exp(np.poly1d(np.polyfit(logA, logB, 1))(logA)))
,但我发现这样更难调试。
以下代码中的其他一些不同之处:
当您从 z
计算 logA
时,您正在使用列表理解来过滤掉任何 < 1 的值,但是 z
是一个线性范围并且仅第一个值是 < 1。从 1 开始创建 z
似乎更容易,这就是我的编码方式。
我不确定为什么您在 logA
的列表理解中有术语 x*log(x)
。这对我来说像是一个错误,所以我没有将其包含在答案中。
此代码对您来说应该可以正常工作:
fig=plt.figure()
ax = fig.add_subplot(111)
z=np.arange(1, len(x)+1) #start at 1, to avoid error from log(0)
logA = np.log(z) #no need for list comprehension since all z values >= 1
logB = np.log(y)
m, c = np.polyfit(logA, logB, 1) # fit log(y) = m*log(x) + c
y_fit = np.exp(m*logA + c) # calculate the fitted values of y
plt.plot(z, y, color = 'r')
plt.plot(z, y_fit, ':')
ax.set_yscale('symlog')
ax.set_xscale('symlog')
#slope, intercept = np.polyfit(logA, logB, 1)
plt.xlabel("Pre_referer")
plt.ylabel("Popularity")
ax.set_title('Pre Referral URL Popularity distribution')
plt.show()
当我运行它在模拟数据上时,我得到下图:
备注:
该行左右两端的 'kinks' 是使用 "symlog" 的结果,它线性化了非常小的值,如 [=29= 的答案中所述] 。如果此数据绘制在 "log-log" 轴上,则拟合数据将是一条直线。
您可能还想阅读此答案:,它解释了如何使用加权来实现 "better" 适合对数转换数据。
我找到了解决这个问题的另一种方法。分享这个,因为它可能会有所帮助。
fig=plt.figure()
ax = fig.add_subplot(111)
z=np.arange(len(x)) + 1
print z
print y
rank = [np.log10(i) for i in z]
freq = [np.log10(i) for i in y]
m, b, r_value, p_value, std_err = stats.linregress(rank, freq)
print "slope: ", m
print "r-squared: ", r_value**2
print "intercept:", b
plt.plot(rank, freq, 'o',color = 'r')
abline_values = [m * i + b for i in rank]
plt.plot(rank, abline_values)
这基本上也实现了objective。它使用统计模块。
这是一个以对数刻度表示的网络 IP 频率排名图。完成这部分后,我尝试使用 Python 2.7 在对数对数刻度上绘制最佳拟合线。我必须使用 matplotlib 的 "symlog" 轴刻度,否则某些值无法正确显示并且某些值会被隐藏。
我正在绘制的数据的 X 值是 URLs,Y 值是 URLs 的对应频率。
我的数据如下所示:
'http://www.bing.com/search?q=d2l&src=IE-TopResult&FORM=IETR02&conversationid= 123 0.00052210688591'
`http://library.uc.ca/ 118 4.57782298326e-05`
`http://www.bing.com/search?q=d2l+uofc&src=IE-TopResult&FORM=IETR02&conversationid= 114 4.30271029472e-06`
`http://www.nature.com/scitable/topicpage/genetics-and-statistical-analysis-34592 109 1.9483268261e-06`
数据包含第一列中的 URL,第二列中包含相应的频率(相同 URL 出现的次数),最后是第三列中传输的字节。首先,我仅使用第一列和第二列进行此分析。总共有 2,465 个 x 值或唯一 URLs.
以下是我的代码
import os
import matplotlib.pyplot as plt
import numpy as np
import math
from numpy import *
import scipy
from scipy.interpolate import *
from scipy.stats import linregress
from scipy.optimize import curve_fit
file = open(filename1, 'r')
lines = file.readlines()
result = {}
x=[]
y=[]
for line in lines:
course,count,size = line.lstrip().rstrip('\n').split('\t')
if course not in result:
result[course] = int(count)
else:
result[course] += int(count)
file.close()
frequency = sorted(result.items(), key = lambda i: i[1], reverse= True)
x=[]
y=[]
i=0
for element in frequency:
x.append(element[0])
y.append(element[1])
z=[]
fig=plt.figure()
ax = fig.add_subplot(111)
z=np.arange(len(x))
print z
logA = [x*np.log(x) if x>=1 else 1 for x in z]
logB = np.log(y)
plt.plot(z, y, color = 'r')
plt.plot(z, np.poly1d(np.polyfit(logA, logB, 1))(z))
ax.set_yscale('symlog')
ax.set_xscale('symlog')
slope, intercept = np.polyfit(logA, logB, 1)
plt.xlabel("Pre_referer")
plt.ylabel("Popularity")
ax.set_title('Pre Referral URL Popularity distribution')
plt.show()
您会看到导入了很多库,因为我已经使用了很多库,但是 none 我的实验产生了预期的结果。所以上面的代码正确地生成了排名图。可以看出,这是红线,但曲线中的蓝线本应是最佳拟合线,但在视觉上是不正确的。这是生成的图表。
这是我期待的图表。第二张图中的虚线是我以某种方式错误绘制的。
关于如何解决这个问题有什么想法吗?
在双对数尺度上沿直线落下的数据遵循 y = c*x^(m)
形式的幂关系。两边取对数,得到拟合的线性方程:
log(y) = m*log(x) + c
调用 np.polyfit(log(x), log(y), 1)
提供 m
和 c
的值。然后,您可以使用这些值来计算 log_y_fit
的拟合值:
log_y_fit = m*log(x) + c
您想要根据原始数据绘制的拟合值是:
y_fit = exp(log_y_fit) = exp(m*log(x) + c)
那么,您遇到的两个问题是:
您正在使用原始 x 坐标计算拟合值,而不是 log(x) 坐标
您正在绘制拟合 y 值的对数而不将它们转换回原始比例
我在下面的代码中通过将 plt.plot(z, np.poly1d(np.polyfit(logA, logB, 1))(z))
替换为:
m, c = np.polyfit(logA, logB, 1) # fit log(y) = m*log(x) + c
y_fit = np.exp(m*logA + c) # calculate the fitted values of y
plt.plot(z, y_fit, ':')
这可以放在一行中:plt.plot(z, np.exp(np.poly1d(np.polyfit(logA, logB, 1))(logA)))
,但我发现这样更难调试。
以下代码中的其他一些不同之处:
当您从
z
计算logA
时,您正在使用列表理解来过滤掉任何 < 1 的值,但是z
是一个线性范围并且仅第一个值是 < 1。从 1 开始创建z
似乎更容易,这就是我的编码方式。我不确定为什么您在
logA
的列表理解中有术语x*log(x)
。这对我来说像是一个错误,所以我没有将其包含在答案中。
此代码对您来说应该可以正常工作:
fig=plt.figure()
ax = fig.add_subplot(111)
z=np.arange(1, len(x)+1) #start at 1, to avoid error from log(0)
logA = np.log(z) #no need for list comprehension since all z values >= 1
logB = np.log(y)
m, c = np.polyfit(logA, logB, 1) # fit log(y) = m*log(x) + c
y_fit = np.exp(m*logA + c) # calculate the fitted values of y
plt.plot(z, y, color = 'r')
plt.plot(z, y_fit, ':')
ax.set_yscale('symlog')
ax.set_xscale('symlog')
#slope, intercept = np.polyfit(logA, logB, 1)
plt.xlabel("Pre_referer")
plt.ylabel("Popularity")
ax.set_title('Pre Referral URL Popularity distribution')
plt.show()
当我运行它在模拟数据上时,我得到下图:
备注:
该行左右两端的 'kinks' 是使用 "symlog" 的结果,它线性化了非常小的值,如 [=29= 的答案中所述] 。如果此数据绘制在 "log-log" 轴上,则拟合数据将是一条直线。
您可能还想阅读此答案:,它解释了如何使用加权来实现 "better" 适合对数转换数据。
我找到了解决这个问题的另一种方法。分享这个,因为它可能会有所帮助。
fig=plt.figure()
ax = fig.add_subplot(111)
z=np.arange(len(x)) + 1
print z
print y
rank = [np.log10(i) for i in z]
freq = [np.log10(i) for i in y]
m, b, r_value, p_value, std_err = stats.linregress(rank, freq)
print "slope: ", m
print "r-squared: ", r_value**2
print "intercept:", b
plt.plot(rank, freq, 'o',color = 'r')
abline_values = [m * i + b for i in rank]
plt.plot(rank, abline_values)
这基本上也实现了objective。它使用统计模块。