频繁模式挖掘中Apriori、FP-Growth和Eclat算法的实现和对比
最近上数据挖掘的课程,其中学习到了频繁模式挖掘这一章,这章介绍了三种算法,Apriori、FP-Growth和Eclat算法;由于对于不同的数据来说,这三种算法的表现不同,所以我们本次就对这三种算法在不同情况下的效率进行对比。从而得出适合相应算法的情况。
GitHub:https://github.com/loyalzc/freqpattern
(一)算法原理
其中相应的算法原理在之前的博客中都有非常详细的介绍,这里就不再赘述,这里给出三种算法大概的介绍
但是这里给出每个算法的关键点:
1.1 Apriori算法:
- 限制候选产生发现频繁项集
- 重要性质:频繁项集所有非空子集也一定是频繁的。
- 主要步骤:
- 连接
- 剪枝
- 特点:需要多次扫描数据库,对于大规模数据效率很低!
Apriori算法原理详细介绍:http://www.cnblogs.com/90zeng/p/apriori.html
1.2 FP-Growth算法
- 通过模式增长挖掘频繁模式
- 主要步骤:
- 构建频繁模式树
- 构造条件模式基
- 挖掘频繁模式
- 特点:两次扫描数据库,采用分治的策略有效降低搜索开销
FP-Growth算法原理详细介绍:http://www.cnblogs.com/datahunter/p/3903413.html
1.3 Eclat算法
- 使用垂直格式挖掘频繁项集
- 主要步骤:
- 将数据倒排{ item:TID_set }
- 通过求频繁k项集的交集来获取k+1项集
- 特点:仅需要一次扫描数据库,TID集合很长的话需要消耗大量的内存和计算时间
Eclat算法原理详细介绍:http://www.cnblogs.com/catkins/p/5270484.html
(二)算法实现
由于各个博客给出的算法实现并不统一,而且本人在实现《机器学习实战》中FP-Growth算法的时候发现,在在创建FP-Tree时根据headTable中元素的支持度顺序的排序过程中,这个地方的排序方法写的有问题,当在模式稠密时,具有很多支持度相同的项集,书中的代码并没有考虑着一点,所以如果遇到支持度相同的项集那个就会出现一定的随机性,导致建树过程出错,最后的频繁项集结果会偏小,因此这里对改错误进行了纠正,在支持度相同时,添加了按照项集排序的规则,这样建立的FP-Tree才完全正确。
1.1 Apriori算法实现:
# -*- coding: utf-8 -*-
'''
@author: Infaraway
@time: 2017/4/15 12:54
@Function:
''' def init_c1(data_set_dict, min_support):
c1 = []
freq_dic = {}
for trans in data_set_dict:
for item in trans:
freq_dic[item] = freq_dic.get(item, 0) + data_set_dict[trans]
# 优化初始的集合,使不满足最小支持度的直接排除
c1 = [[k] for (k, v) in freq_dic.iteritems() if v >= min_support]
c1.sort()
return map(frozenset, c1) def scan_data(data_set, ck, min_support, freq_items):
"""
计算Ck中的项在数据集合中的支持度,剪枝过程
:param data_set:
:param ck:
:param min_support: 最小支持度
:param freq_items: 存储满足支持度的频繁项集
:return:
"""
ss_cnt = {}
# 每次遍历全体数据集
for trans in data_set:
for item in ck:
# 对每一个候选项集, 检查是否是 term中的一部分(子集),即候选项能否得到支持
if item.issubset(trans):
ss_cnt[item] = ss_cnt.get(item, 0) + 1
ret_list = []
for key in ss_cnt:
support = ss_cnt[key] # 每个项的支持度
if support >= min_support:
ret_list.insert(0, key) # 将满足最小支持度的项存入集合
freq_items[key] = support #
return ret_list def apriori_gen(lk, k):
"""
由Lk的频繁项集生成新的候选项集 连接过程
:param lk: 频繁项集集合
:param k: k 表示集合中所含的元素个数
:return: 候选项集集合
"""
ret_list = []
for i in range(len(lk)):
for j in range(i+1, len(lk)):
l1 = list(lk[i])[:k-2]
l2 = list(lk[j])[:k-2]
l1.sort()
l2.sort()
if l1 == l2:
ret_list.append(lk[i] | lk[j]) # 求并集
# retList.sort()
return ret_list def apriori_zc(data_set, data_set_dict, min_support=5):
"""
Apriori算法过程
:param data_set: 数据集
:param min_support: 最小支持度,默认值 0.5
:return:
"""
c1 = init_c1(data_set_dict, min_support)
data = map(set, data_set) # 将dataSet集合化,以满足scanD的格式要求
freq_items = {}
l1 = scan_data(data, c1, min_support, freq_items) # 构建初始的频繁项集
l = [l1]
# 最初的L1中的每个项集含有一个元素,新生成的项集应该含有2个元素,所以 k=2
k = 2
while len(l[k - 2]) > 0:
ck = apriori_gen(l[k - 2], k)
lk = scan_data(data, ck, min_support, freq_items)
l.append(lk)
k += 1 # 新生成的项集中的元素个数应不断增加
return freq_items
1.2 FP-Growth算法实现:
1)FP_Growth文件:
在create_tree()函数中修改《机器学习实战》中的代码:
##############################################################################################
# 这里修改机器学习实战中的排序代码:
ordered_items = [v[0] for v in sorted(local_data.items(), key=lambda kv: (-kv[1], kv[0]))]
##############################################################################################
# -*- coding: utf-8 -*-
"""
@author: Infaraway
@time: 2017/4/15 16:07
@Function:
"""
from DataMining.Unit6_FrequentPattern.FP_Growth.TreeNode import treeNode def create_tree(data_set, min_support=1):
"""
创建FP树
:param data_set: 数据集
:param min_support: 最小支持度
:return:
"""
freq_items = {} # 频繁项集
for trans in data_set: # 第一次遍历数据集
for item in trans:
freq_items[item] = freq_items.get(item, 0) + data_set[trans] header_table = {k: v for (k, v) in freq_items.iteritems() if v >= min_support} # 创建头指针表
# for key in header_table:
# print key, header_table[key] # 无频繁项集
if len(header_table) == 0:
return None, None
for k in header_table:
header_table[k] = [header_table[k], None] # 添加头指针表指向树中的数据
# 创建树过程
ret_tree = treeNode('Null Set', 1, None) # 根节点 # 第二次遍历数据集
for trans, count in data_set.items():
local_data = {}
for item in trans:
if header_table.get(item, 0):
local_data[item] = header_table[item][0]
if len(local_data) > 0:
##############################################################################################
# 这里修改机器学习实战中的排序代码:
ordered_items = [v[0] for v in sorted(local_data.items(), key=lambda kv: (-kv[1], kv[0]))]
##############################################################################################
update_tree(ordered_items, ret_tree, header_table, count) # populate tree with ordered freq itemset
return ret_tree, header_table def update_tree(items, in_tree, header_table, count):
'''
:param items: 元素项
:param in_tree: 检查当前节点
:param header_table:
:param count:
:return:
'''
if items[0] in in_tree.children: # check if ordered_items[0] in ret_tree.children
in_tree.children[items[0]].increase(count) # incrament count
else: # add items[0] to in_tree.children
in_tree.children[items[0]] = treeNode(items[0], count, in_tree)
if header_table[items[0]][1] is None: # update header table
header_table[items[0]][1] = in_tree.children[items[0]]
else:
update_header(header_table[items[0]][1], in_tree.children[items[0]])
if len(items) > 1: # call update_tree() with remaining ordered items
update_tree(items[1::], in_tree.children[items[0]], header_table, count) def update_header(node_test, target_node):
'''
:param node_test:
:param target_node:
:return:
'''
while node_test.node_link is not None: # Do not use recursion to traverse a linked list!
node_test = node_test.node_link
node_test.node_link = target_node def ascend_tree(leaf_node, pre_fix_path):
'''
遍历父节点,找到路径
:param leaf_node:
:param pre_fix_path:
:return:
'''
if leaf_node.parent is not None:
pre_fix_path.append(leaf_node.name)
ascend_tree(leaf_node.parent, pre_fix_path) def find_pre_fix_path(base_pat, tree_node):
'''
创建前缀路径
:param base_pat: 频繁项
:param treeNode: FP树中对应的第一个节点
:return:
'''
# 条件模式基
cond_pats = {}
while tree_node is not None:
pre_fix_path = []
ascend_tree(tree_node, pre_fix_path)
if len(pre_fix_path) > 1:
cond_pats[frozenset(pre_fix_path[1:])] = tree_node.count
tree_node = tree_node.node_link
return cond_pats def mine_tree(in_tree, header_table, min_support, pre_fix, freq_items):
'''
挖掘频繁项集
:param in_tree:
:param header_table:
:param min_support:
:param pre_fix:
:param freq_items:
:return:
'''
# 从小到大排列table中的元素,为遍历寻找频繁集合使用
bigL = [v[0] for v in sorted(header_table.items(), key=lambda p: p[1])] # (sort header table)
for base_pat in bigL: # start from bottom of header table
new_freq_set = pre_fix.copy()
new_freq_set.add(base_pat)
# print 'finalFrequent Item: ',new_freq_set #append to set
if len(new_freq_set) > 0:
freq_items[frozenset(new_freq_set)] = header_table[base_pat][0]
cond_patt_bases = find_pre_fix_path(base_pat, header_table[base_pat][1])
my_cond_tree, my_head = create_tree(cond_patt_bases, min_support)
# print 'head from conditional tree: ', my_head
if my_head is not None: # 3. mine cond. FP-tree
# print 'conditional tree for: ',new_freq_set
# my_cond_tree.disp(1)
mine_tree(my_cond_tree, my_head, min_support, new_freq_set, freq_items) def fp_growth(data_set, min_support=1):
my_fp_tree, my_header_tab = create_tree(data_set, min_support)
# my_fp_tree.disp()
freq_items = {}
mine_tree(my_fp_tree, my_header_tab, min_support, set([]), freq_items)
return freq_items
2)treeNode对象文件
# -*- coding: utf-8 -*-
'''
@author: Infaraway
@time: 2017/3/31 0:14
@Function:
''' class treeNode:
def __init__(self, name_value, num_occur, parent_node):
self.name = name_value # 节点元素名称
self.count = num_occur # 出现的次数
self.node_link = None # 指向下一个相似节点的指针,默认为None
self.parent = parent_node # 指向父节点的指针
self.children = {} # 指向孩子节点的字典 子节点的元素名称为键,指向子节点的指针为值 def increase(self, num_occur):
"""
增加节点的出现次数
:param num_occur: 增加数量
:return:
"""
self.count += num_occur def disp(self, ind=1):
print ' ' * ind, self.name, ' ', self.count
for child in self.children.values():
child.disp(ind + 1)
1.3 Eclat算法实现
# -*- coding: utf-8 -*-
"""
@author: Infaraway
@time: 2017/4/15 19:33
@Function:
""" import sys
import time
type = sys.getfilesystemencoding() def eclat(prefix, items, min_support, freq_items):
while items:
# 初始遍历单个的元素是否是频繁
key, item = items.pop()
key_support = len(item)
if key_support >= min_support:
# print frozenset(sorted(prefix+[key]))
freq_items[frozenset(sorted(prefix+[key]))] = key_support
suffix = [] # 存储当前长度的项集
for other_key, other_item in items:
new_item = item & other_item # 求和其他集合求交集
if len(new_item) >= min_support:
suffix.append((other_key, new_item))
eclat(prefix+[key], sorted(suffix, key=lambda item: len(item[1]), reverse=True), min_support, freq_items)
return freq_items def eclat_zc(data_set, min_support=1):
"""
Eclat方法
:param data_set:
:param min_support:
:return:
"""
# 将数据倒排
data = {}
trans_num = 0
for trans in data_set:
trans_num += 1
for item in trans:
if item not in data:
data[item] = set()
data[item].add(trans_num)
freq_items = {}
freq_items = eclat([], sorted(data.items(), key=lambda item: len(item[1]), reverse=True), min_support, freq_items)
return freq_items
(三)试验阶段:
这样我们就统一了三种算法的调用以及返回值,现在我们可以开始试验阶段了,我们在试验阶段分别根据最小支持度阈值和数据规模的变化来判断这三种算法的效率:
首先我们先统一调用者三个算法:
def test_fp_growth(minSup, dataSetDict, dataSet):
freqItems = fp_growth(dataSetDict, minSup)
freqItems = sorted(freqItems.iteritems(), key=lambda item: item[1])
return freqItems def test_apriori(minSup, dataSetDict, dataSet):
freqItems = apriori_zc(dataSet, dataSetDict, minSup)
freqItems = sorted(freqItems.iteritems(), key=lambda item: item[1])
return freqItems def test_eclat(minSup, dataSetDict, dataSet):
freqItems = eclat_zc(dataSet, minSup)
freqItems = sorted(freqItems.iteritems(), key=lambda item: item[1])
return freqItems
然后实现数据规模变化的效率改变
def do_experiment_min_support(): data_name = 'unixData8_pro.txt'
x_name = "Min_Support"
data_num = 1500
minSup = data_num / 6 dataSetDict, dataSet = loadDblpData(open("dataSet/" + data_name), ',', data_num)
step = minSup / 5 # #################################################################
all_time = []
x_value = []
for k in range(5): x_value.append(minSup) # #################################################################
if minSup < 0: # #################################################################
break
time_fp = 0
time_et = 0
time_ap = 0
freqItems_fp = {}
freqItems_eclat = {}
freqItems_ap = {}
for i in range(10):
ticks0 = time.time()
freqItems_fp = test_fp_growth(minSup, dataSetDict, dataSet)
time_fp += time.time() - ticks0
ticks0 = time.time()
freqItems_eclat = test_eclat(minSup, dataSetDict, dataSet)
time_et += time.time() - ticks0
ticks0 = time.time()
freqItems_ap = test_apriori(minSup, dataSetDict, dataSet)
time_ap += time.time() - ticks0
print "minSup :", minSup, " data_num :", data_num, \
" freqItems_fp:", len(freqItems_fp), " freqItems_eclat:", len(freqItems_eclat), " freqItems_ap:", len(
freqItems_ap)
print "fp_growth:", time_fp / 10, " eclat:", time_et / 10, " apriori:", time_ap / 10
# print_freqItems("show", freqItems_eclat)
minSup -= step # #################################################################
use_time = [time_fp / 10, time_et / 10, time_ap / 10]
all_time.append(use_time)
# print use_time
y_value = []
for i in range(len(all_time[0])):
tmp = []
for j in range(len(all_time)):
tmp.append(all_time[j][i])
y_value.append(tmp)
plot_pic(x_value, y_value, data_name, x_name)
return x_value, y_value
然后实现最小支持度变化的效率改变
def do_experiment_data_size(): data_name = 'kosarakt.txt'
x_name = "Data_Size"
data_num = 200000 step = data_num / 5 # #################################################################
all_time = []
x_value = []
for k in range(5):
minSup = data_num * 0.010
dataSetDict, dataSet = loadDblpData(open("dataSet/"+data_name), ' ', data_num)
x_value.append(data_num) # #################################################################
if data_num < 0: # #################################################################
break
time_fp = 0
time_et = 0
time_ap = 0
freqItems_fp = {}
freqItems_eclat = {}
freqItems_ap = {}
for i in range(2):
ticks0 = time.time()
freqItems_fp = test_fp_growth(minSup, dataSetDict, dataSet)
time_fp += time.time() - ticks0
ticks0 = time.time()
freqItems_eclat = test_eclat(minSup, dataSetDict, dataSet)
time_et += time.time() - ticks0
ticks0 = time.time()
# freqItems_ap = test_apriori(minSup, dataSetDict, dataSet)
# time_ap += time.time() - ticks0
print "minSup :", minSup, " data_num :", data_num, \
" freqItems_fp:", len(freqItems_fp), " freqItems_eclat:", len(freqItems_eclat), " freqItems_ap:", len(freqItems_ap)
print "fp_growth:", time_fp / 10, " eclat:", time_et / 10, " apriori:", time_ap / 10
# print_freqItems("show", freqItems_eclat)
data_num -= step # #################################################################
use_time = [time_fp / 10, time_et / 10, time_ap / 10]
all_time.append(use_time)
# print use_time y_value = []
for i in range(len(all_time[0])):
tmp = []
for j in range(len(all_time)):
tmp.append(all_time[j][i])
y_value.append(tmp)
plot_pic(x_value, y_value, data_name, x_name)
return x_value, y_value
同时为了观察方便,我们需要对三种算法返回的结果进行绘图
# -*- coding: utf-8 -*-
"""
@author: Infaraway
@time: 2017/4/16 20:48
@Function:
""" import matplotlib.pyplot as plt def plot_pic(x_value, y_value, title, x_name):
plot1 = plt.plot(x_value, y_value[0], 'r', label='Kulc') # use pylab to plot x and y
plot2 = plt.plot(x_value, y_value[1], 'g', label='IR') # use pylab to plot x and y
# plot3 = plt.plot(x_value, y_value[2], 'b', label='Apriori') # use pylab to plot x and y
plt.title(title) # give plot a title
plt.xlabel(x_name) # make axis labels
plt.ylabel('value ')
plt.legend(loc='upper right') # make legend plt.show() # show the plot on the screen
将两个部分统一执行:
if __name__ == '__main__': # x_value, y_value = do_experiment_min_support()
# x_value, y_value = do_experiment_data_size()
# do_test()
(四)实验结果分析:
本次实验我们主要从以下几个方面来讨论三种算法的效率:
- 数据规模大小
- 最小支持度阈值
- 长事物数据
- 模式的稠密性
4.1 数据规模大小:
数据集:unxiData8
规模:900-1500
Min_support = 1/30时 Min_support = 1/20时
数据集:kosarakt
规模:6000-10000
Min_support = 1/50 Min_support = 1/80 Min_support = 1/100
结论:一般情况下,数据规模越大,使用Apriori算法的效率越低,因为该算法需要多次扫描数据库,当数据量越大时,扫描数据库带来的消耗越多。
4.2 最小支持度大小
数据集:unixData8
支持度:4% - 20%
Data_size = 500 Data_size = 1000 Data_size = 1500
数据集:kosarakt
支持度:1% - 2%
Data_size = 3000 Data_size = 5000 Data_size = 10000
结论:
- 最小支持度越小,各个算法所花费的时间越多且Apriori算法效率最差;
- Apriori算法候选项集较多,扫描数据库次数较多
- FP-Growth算法FP树较茂盛,求解模式更多
- Eclat算法求交集次数较多
- 随着最小支持度的增加,各个算法的效率逐渐接近,相当于数据量较小的情况。
4.3 长事物数据:
数据集:movieItem DataSize = 943
特点:单个事务数据比较长, 大量单个条目达到500个项(但频繁模式并不长)
Min_support = 1/4 Min_support = 1/6 Min_support = 1/8
结论:对于长事物的数据集来说
- 最小支持度较大时,此时由于生成的FP树深度较大,求解的子问题较多,FP算法性能显著下降;
- 最小支持度较小时,此时Apriori算法生成大量候选项,扫描数据库次数显著增加,Apriori算法性能显著下降
4.4 模式稠密性:
数据集:movieItem
特点:单个事物间相似度很大(导致频繁模式特别多且比较长)
Min_support = 0.8 Min_support = 0.9
结论: 对于模式比较稠密的数据集来说,由于会产生特别多而且比较长的模式,三种算法的效率均有下降,其中FP-Growth算法中FP树层次较深,会产生较多的子问题,Eclat算法则需要进行大量的求交集运算,并且消耗大量的存储空间,Apriori算法则需要更多次的扫描数据库,因此效率最低。
(五)总结:
从上面实验可以看到,Apriori算法的效率最低,因为他需要很多次的扫描数据库;其次FP—Growth算法在长事物数据上表现很差,因为当事物很长时,树的深度也很大,需要求解的子问题就变得特别多,因此效率会迅速下降;Eclat算法的效率最高,但是由于我们事先使用了递归的思想,当数据量很大的时候会给系统带来巨大的负担,因此不适合数据量很大的情况;当然有一种叫做diffset的技术可以解决Eclat算法的不足,这里我们不做讨论!
本次实验的所有代码和数据下载:链接:http://pan.baidu.com/s/1jHAT7cq 密码:21pb
频繁模式挖掘中Apriori、FP-Growth和Eclat算法的实现和对比的更多相关文章
- 频繁模式挖掘中Apriori、FP-Growth和Eclat算法的实现和对比(Python实现)
最近上数据挖掘的课程,其中学习到了频繁模式挖掘这一章,这章介绍了三种算法,Apriori.FP-Growth和Eclat算法:由于对于不同的数据来说,这三种算法的表现不同,所以我们本次就对这三种算法在 ...
- 【甘道夫】并行化频繁模式挖掘算法FP Growth及其在Mahout下的命令使用
今天调研了并行化频繁模式挖掘算法PFP Growth及其在Mahout下的命令使用,简单记录下试验结果,供以后查阅: 环境:Jdk1.7 + Hadoop2.2.0单机伪集群 + Mahout0.6 ...
- 八、频繁模式挖掘Frequent Pattern Mining
频繁模式挖掘(Frequent Pattern Mining): 频繁项集挖掘是通常是大规模数据分析的第一步,多年以来它都是数据挖掘领域的活跃研究主题.建议用户参考维基百科的association r ...
- Frequent Pattern 挖掘之二(FP Growth算法)
Frequent Pattern 挖掘之二(FP Growth算法) FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断 ...
- 频繁模式挖掘apriori算法介绍及Java实现
频繁模式是频繁地出如今数据集中的模式(如项集.子序列或者子结构).比如.频繁地同一时候出如今交易数据集中的商品(如牛奶和面包)的集合是频繁项集. 一些基本概念 支持度:support(A=>B) ...
- 频繁模式挖掘 Apriori算法 FP-tree
啤酒 尿布 组合营销 X=>Y,其中x属于项集I,Y属于项集I,且X.Y的交集等于空集. 2类算法 Apriori算法 不断地构造候选集.筛选候选集来挖掘出频繁项集,需要多次扫描原始数据.磁盘I ...
- Frequent Pattern 挖掘之二(FP Growth算法)(转)
FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断得扫描整个数据库进行比对.为了达到这样的效果,它采用了一种简洁的数据结 ...
- 数据挖掘(七):Apriori算法:频繁模式挖掘
1 算法思想 算法使用频繁项集性质的先验知识.Apriori使用一种称作逐层搜索的迭代方法,k项集用于探索(k+1)项集.首先,通过扫描数据库,累积每个项的计数,并收集满足最小支持度的项,找出频繁1项 ...
- 频繁项挖掘算法Apriori和FGrowth
一:背景介绍 最近在公司用spark的平台做了一个购物车的推荐,用到的算法主要是FGrowth算法,它是Apriori算法的升级版,算法的主要目的是找出频繁进行一起购买的商品.本文主要介绍两个算法的背 ...
随机推荐
- Python模块发布
项目根目录添加setup.py模块: from distutils.core import setup setup( name="模块名字", version="1.0. ...
- 让CEF支持FLASH(非安装插件的形式)
测试环境: CEF3 + CefGlue 下载FLASH的NPAPI DLL文件 , 在CEF目录下新建文件夹plugins,然后把DLL文件放进去即可. 据说下面是PPAPI的方式,未测试 启动的时 ...
- 【CNMP系列】VIM编辑器详解
缘起 大学的时候做过Linux内核驱动程序研发,之前写C语言就是用的Vim编辑器,当年的Vim还不如今天之强大,当时的插件也没有现在这么多,只是觉得这个编辑器能满足我想要的所有,查看Linux内核代码 ...
- KeychainItemWrapper的使用
KeychinaItemWrapper官方Demo下载地址KeychinaItemWrapper. NSString *identifier = @"xxxxxx";//你要使用的 ...
- pcntl_fork 导致 MySQL server has gone away 解决方案
pcntl_fork 前连数据库,就会报 MySQL server has gone away 错误.原因是子进程会继承主进程的数据库连接,当mysql返回数据时,这些子进程都可以通过这个连接读到数据 ...
- Scut游戏引擎改造兼容Codis。
原生的Scut引擎是采用redis来做数据缓存层,引擎在以异步的方式(时间可配置,默认100ms)实现数据同步.为了提高redis的可扩展性.高可用性,把redis换成codis,因为codis有部分 ...
- C#文件上传类,文件流,字节数组等
using System;using System.IO;using System.Web;using System.Web.UI.WebControls; namespace DotNet.Util ...
- iOS开发之状态栏
从iOS7开始,状态栏默认情况下归控制器管理,比如状态栏的样式.状态栏的是否可见 控制器通过重写以下方法来控制状态栏 设置状态栏的样式,只需重写下列方法即可: - (UIStatusBarStyle) ...
- nginx浏览目录
[root@localhost domains]# vi web.jd.com location / proxy_set_header X-Forwarded-For $proxy_add_x_for ...
- MySQL从库忽略某些错误
z熬配置MySQL主从同步的时候常常会因为主库的中SQL语句的错误造成从库的同步出现错误,一旦从库同步出现错误就会造成同步的卡壳影响后续的同步: 可以在从库的配置文件中加入如下的参数,使从库可以自动忽 ...