(Newman and Gievan 2004) A community is a subgraph containing nodes which are more densely linked to each other than to the rest of the graph or equivalently, a graph has a community structure if the number of links into any subgraph is higher than the number of links between those subgraphs.
接下来的问题是,什么样的metrics可以用来描述这种density?Louvian 定义了一个数值上的概念(本质上就是一个目标函数),有了这个目标函数,就可以引出接下来要讨论的 method based on modularity optimization
要注意的,社区划分有很多不同的算法,本文讨论的 Fast Unfolding(Louvian)只是其中一种,而这种所谓的density密度评估方法也其实其中一种思想,不要固话地认为社区划分就只有这一种方法。
Relevant Link:
https://stackoverflow.com/questions/21814235/how-can-modularity-help-in-network-analysis http://iopscience.iop.org/article/10.1088/1742-5468/2008/10/P10008/fulltext/https://www.researchgate.net/publication/1913681_Fast_Unfolding_of_Communities_in_Large_Networks?enrichId=rgreq-d403e26a5cb211b7053c36946c71acb3-XXX&enrichSource=Y292ZXJQYWdlOzE5MTM2ODE7QVM6MTAxOTUyNjc5NTc5NjY3QDE0MDEzMTg4MjE3ODA=⪙=1_x_3&_esc=publicationCoverPdfhttps://www.jianshu.com/p/4ebe42dfa8echttps://blog.csdn.net/u011089523/article/details/79090453https://blog.csdn.net/google19890102/article/details/48660239《Fast Unfolding of Communities in Large Networks》
如果按照启发式/贪婪思想进行”one-step one node“的社区聚类,O9、O10、O11会被先加入到社区D中,因为在每次这样的迭代中,D社区内部的紧密度(不管基于node密度还是edge得modularity评估)都是不断提高,符合算法的check条件,因此,O9、O10、O11会被加入到社区D中。
并且计算节点 i 分配到其邻居 j 时,模块度的变化只与节点 i、j 的社区有关,与其他社区无关,因此计算很快。
在论文中,把节点 i 分配到邻居节点 j 所在的社区 c 时模块度变化为:
5. A Python implementation of the Louvain method to find communities in large networks
#!/usr/bin/env python3
# -*- coding: utf-8 -*-''' Implements the Louvain method.
Input: a weighted undirected graph
Ouput: a (partition, modularity) pair where modularity is maximum'''class PyLouvain: ''' Builds a graph from _path.
_path: a path to a file containing "node_from node_to" edges (one per line) ''' @classmethod
def from_file(cls, path):
f = open(path, 'r')
lines = f.readlines()
f.close()
nodes = {}
edges = [] for line in lines:
n = line.split() if not n: break
nodes[n[0]] = 1
nodes[n[1]] = 1
w = 1
if len(n) == 3:
w = int(n[2])
edges.append(((n[0], n[1]), w))
# rebuild graph with successive identifiers
nodes_, edges_ = in_order(nodes, edges)
print("%d nodes, %d edges" % (len(nodes_), len(edges_))) return cls(nodes_, edges_) ''' Builds a graph from _path.
_path: a path to a file following the Graph Modeling Language specification ''' @classmethod
def from_gml_file(cls, path):
f = open(path, 'r')
lines = f.readlines()
f.close()
nodes = {}
edges = []
current_edge = (-1, -1, 1)
in_edge = 0
for line in lines:
words = line.split() if not words: break
if words[0] == 'id':
nodes[int(words[1])] = 1
elif words[0] == 'source':
in_edge = 1
current_edge = (int(words[1]), current_edge[1], current_edge[2])
elif words[0] == 'target' and in_edge:
current_edge = (current_edge[0], int(words[1]), current_edge[2])
elif words[0] == 'value' and in_edge:
current_edge = (current_edge[0], current_edge[1], int(words[1]))
elif words[0] == ']' and in_edge:
edges.append(((current_edge[0], current_edge[1]), 1))
current_edge = (-1, -1, 1)
in_edge = 0
nodes, edges = in_order(nodes, edges)
print("%d nodes, %d edges" % (len(nodes), len(edges))) return cls(nodes, edges) ''' Initializes the method.
_nodes: a list of ints
_edges: a list of ((int, int), weight) pairs ''' def __init__(self, nodes, edges):
self.nodes = nodes
self.edges = edges
# precompute m (sum of the weights of all links in network)
# k_i (sum of the weights of the links incident to node i)
self.m = 0
self.k_i = [0 for n in nodes]
self.edges_of_node = {}
self.w = [0 for n in nodes] for e in edges:
self.m += e[1]
self.k_i[e[0][0]] += e[1]
self.k_i[e[0][1]] += e[1] # there's no self-loop initially # save edges by node if e[0][0] not in self.edges_of_node:
self.edges_of_node[e[0][0]] = [e] else:
self.edges_of_node[e[0][0]].append(e) if e[0][1] not in self.edges_of_node:
self.edges_of_node[e[0][1]] = [e]
elif e[0][0] != e[0][1]:
self.edges_of_node[e[0][1]].append(e)
# access community of a node in O(1) time
self.communities = [n for n in nodes]
self.actual_partition = [] ''' Applies the Louvain method. ''' def apply_method(self):
network = (self.nodes, self.edges)
best_partition = [[node] for node in network[0]]
best_q = -1
i = 1
while 1:
i += 1
partition = self.first_phase(network)
q = self.compute_modularity(partition)
partition = [c for c in partition if c]
# clustering initial nodes with partition if self.actual_partition:
actual = [] for p in partition:
part = [] for n in p:
part.extend(self.actual_partition[n])
actual.append(part)
self.actual_partition = actual else:
self.actual_partition = partition if q == best_q: # 如果本轮迭代modularity没有改变,则认为收敛,停止 break
network = self.second_phase(network, partition)
best_partition = partition
best_q = q return (self.actual_partition, best_q) ''' Computes the modularity of the current network.
_partition: a list of lists of nodes ''' def compute_modularity(self, partition):
q = 0
m2 = self.m * 2
for i in range(len(partition)):
q += self.s_in[i] / m2 - (self.s_tot[i] / m2) ** 2
return q ''' Computes the modularity gain of having node in community _c.
_node: an int
_c: an int
_k_i_in: the sum of the weights of the links from _node to nodes in _c ''' def compute_modularity_gain(self, node, c, k_i_in): return 2 * k_i_in - self.s_tot[c] * self.k_i[node] / self.m ''' Performs the first phase of the method.
_network: a (nodes, edges) pair ''' def first_phase(self, network):
# make initial partition
best_partition = self.make_initial_partition(network) while 1:
improvement = 0
for node in network[0]:
node_community = self.communities[node]
# default best community is its own
best_community = node_community
best_gain = 0
# remove _node from its community
best_partition[node_community].remove(node)
best_shared_links = 0
for e in self.edges_of_node[node]: if e[0][0] == e[0][1]: continue
if e[0][0] == node and self.communities[e[0][1]] == node_community or e[0][1] == node and self.communities[e[0][0]] == node_community:
best_shared_links += e[1]
self.s_in[node_community] -= 2 * (best_shared_links + self.w[node])
self.s_tot[node_community] -= self.k_i[node]
self.communities[node] = -1
communities = {} # only consider neighbors of different communities for neighbor in self.get_neighbors(node):
community = self.communities[neighbor] if community in communities: continue
communities[community] = 1
shared_links = 0
for e in self.edges_of_node[node]: if e[0][0] == e[0][1]: continue
if e[0][0] == node and self.communities[e[0][1]] == community or e[0][1] == node and self.communities[e[0][0]] == community:
shared_links += e[1]
# compute modularity gain obtained by moving _node to the community of _neighbor
gain = self.compute_modularity_gain(node, community, shared_links) if gain > best_gain:
best_community = community
best_gain = gain
best_shared_links = shared_links
# insert _node into the community maximizing the modularity gain
best_partition[best_community].append(node)
self.communities[node] = best_community
self.s_in[best_community] += 2 * (best_shared_links + self.w[node])
self.s_tot[best_community] += self.k_i[node] if node_community != best_community:
improvement = 1
if not improvement: break
return best_partition ''' Yields the nodes adjacent to _node.
_node: an int
''' def get_neighbors(self, node): for e in self.edges_of_node[node]: if e[0][0] == e[0][1]: # a node is not neighbor with itself continue
if e[0][0] == node: yield e[0][1] if e[0][1] == node: yield e[0][0] ''' Builds the initial partition from _network.
_network: a (nodes, edges) pair ''' def make_initial_partition(self, network):
partition = [[node] for node in network[0]]
self.s_in = [0 for node in network[0]]
self.s_tot = [self.k_i[node] for node in network[0]] for e in network[1]: if e[0][0] == e[0][1]: # only self-loops
self.s_in[e[0][0]] += e[1]
self.s_in[e[0][1]] += e[1] return partition ''' Performs the second phase of the method.
_network: a (nodes, edges) pair
_partition: a list of lists of nodes ''' def second_phase(self, network, partition):
nodes_ = [i for i in range(len(partition))]
# relabelling communities
communities_ = []
d = {}
i = 0
for community in self.communities: if community in d:
communities_.append(d[community]) else:
d[community] = i
communities_.append(i)
i += 1
self.communities = communities_
# building relabelled edges
edges_ = {} for e in network[1]:
ci = self.communities[e[0][0]]
cj = self.communities[e[0][1]] try:
edges_[(ci, cj)] += e[1]
except KeyError:
edges_[(ci, cj)] = e[1]
edges_ = [(k, v) for k, v in edges_.items()]
# recomputing k_i vector and storing edges by node
self.k_i = [0 for n in nodes_]
self.edges_of_node = {}
self.w = [0 for n in nodes_] for e in edges_:
self.k_i[e[0][0]] += e[1]
self.k_i[e[0][1]] += e[1] if e[0][0] == e[0][1]:
self.w[e[0][0]] += e[1] if e[0][0] not in self.edges_of_node:
self.edges_of_node[e[0][0]] = [e] else:
self.edges_of_node[e[0][0]].append(e) if e[0][1] not in self.edges_of_node:
self.edges_of_node[e[0][1]] = [e]
elif e[0][0] != e[0][1]:
self.edges_of_node[e[0][1]].append(e)
# resetting communities
self.communities = [n for n in nodes_] return (nodes_, edges_)''' Rebuilds a graph with successive nodes' ids.
_nodes: a dict of int
_edges: a list of ((int, int), weight) pairs'''def in_order(nodes, edges):
# rebuild graph with successive identifiers
nodes = list(nodes.keys())
nodes.sort()
i = 0
nodes_ = []
d = {} for n in nodes:
nodes_.append(i)
d[n] = i
i += 1
edges_ = [] for e in edges:
edges_.append(((d[e[0][0]], d[e[0][1]]), e[1])) return (nodes_, edges_)
社区发现的最后一轮结果为:
以polbooks.gml为例,16、17、18、19被归类为同一个社区:
node
[
id 16
label "Betrayal"
value "c"
]
node
[
id 17
label "Shut Up and Sing"
value "c"
]
node
[
id 18
label "Meant To Be"
value "n"
]
node
[
id 19
label "The Right Man"
value "c"
]
1. 社团划分
0x1:社区是什么
在社交网络中,用户相当于每一个点,用户之间通过互相的关注关系构成了整个网络的结构。
在这样的网络中,有的用户之间的连接较为紧密,有的用户之间的连接关系较为稀疏。其中连接较为紧密的部分可以被看成一个社区,其内部的节点之间有较为紧密的连接,而在两个社区间则相对连接较为稀疏。
整个整体的结构被称为社团结构。如下图,红色的黑色的点集呈现出社区的结构,
用红色的点和黑色的点对其进行标注,整个网络被划分成了两个部分,其中,这两个部分的内部连接较为紧密,而这两个社区之间的连接则较为稀疏。
如何去划分上述的社区便称为社区划分的问题。
0x2:社区划分的出发点和意图
直观地说,community detection的一般目标是要探测网络中的“块”cluster或是“社团”community。
这么做的目的和效果有许多,比如说机房里机器的连接方式,这里形成了网络结构,那么,哪些机器可以视作一个“块”?进一步地,什么样的连接方式才有比较高的稳定性呢?如果我们想要让这组服务瘫痪,选择什么样的目标呢?
我们再看一个例子,word association network。即词的联想/搭配构成的网络:
我们用不同的颜色对community进行标记,可以看到这种detection得到的结果很有意思。
这个网络从词bright开始进行演化,到后面分别形成了4个组:Colors, Light, Astronomy & Intelligence。
可以说以上这4个词可以较好地概括其所在community的特点(有点聚类的感觉);另外,community中心的词,比如color, Sun, Smart也有很好的代表性(自动提取摘要)。
同时我们注意到,那些处在交叠位置的词呢,比如Bright、light等词,他们是同义项比较多的词。这个图也揭示出了这一层含义。
0x3:节点间存在连接的抽象本质 - 逻辑拓朴结构
社区的节点间是网络拓朴结构,即节点间是存在拓朴连接结构的,我们不能将其和欧式空间或者P空间中的点向量集合空间混为一谈。
以欧式空间为例,不同的节点向量存在于不同的空间位置中,向量夹角近的点向量彼此距离近,而向量夹角远的向量彼此距离远。但是即使是欧式距离很近的向量点,也不一定就代表这它们之间存在拓朴连接关系,只能说在一定的度量下(例如欧式距离度量),这两个节点很相近。
但是在社区结构中,节点之间没有什么空间位置的概念。相对的,节点间存在的是一种逻辑拓朴结构,即存在一种共有关系。
存在共有关系的节点在逻辑上会聚集为一个社区,而社区之前不存在或者存在很弱的共有关系,则呈现分离的逻辑拓朴结构。
读者朋友一定要注意不要用空间结构的概念来试图理解社区结构,不然会陷入理解的困境,社区中的节点只是因为逻辑上的共有关系而聚集在一起而已,彼此之间的位置也没有实际意义,而社区族群之间的分离也是表达一种逻辑上的弱共有关系。
举一些实际的例子:
0x4:什么时候可以使用社区发现算法
下面这句话很明确地说明了在什么业务场景下可以使用社区发现算法:
即我们需要先确定要解决的业务场景中,存在明显的聚集规律,节点(可以是抽象的)之间形成一定的族群结构,而不是呈现无规律的随机分散。同时另一方面,这种聚集的结构是“有意义的”,这里所谓的有意义是指这种聚集本身可以翻译为一定的上层业务场景的表现。
但是很多时候,我们业务场景中的数据集之间的共有关系并不是表现的很明显,即节点之间互相都或多或少存在一些共有关系,这样直接进行社区发现效果肯定是不好的。
所以一个很重要点是,我们在进行社区发现之前,一定要进行数据降噪。
理想情况下,降噪后得到的数据集已经是社区完全内聚,社区间完全零连接,这样pylouvain只要一轮运行就直接得到结果。当然实际场景中不可能有这么好的情况,数据源质量,专家经验的丰富程度等等都会影响降噪的效果,一般情况下,降噪只要能cutoff 90%以上的噪音,pylouvain就基本能通过几轮的迭代完成整体的社区发现过程。
0x5:社区划分的思路概要
什么样的结构能成为团?一种很直观的想法是,同一团内的节点连接更紧密,即具有更大的density。
接下来的问题是,什么样的metrics可以用来描述这种density?Louvian 定义了一个数值上的概念(本质上就是一个目标函数),有了这个目标函数,就可以引出接下来要讨论的 method based on modularity optimization
要注意的,社区划分有很多不同的算法,本文讨论的 Fast Unfolding(Louvian)只是其中一种,而这种所谓的density密度评估方法也其实其中一种思想,不要固话地认为社区划分就只有这一种方法。
Relevant Link:
2. LOUVAIN算法模型
Louvain算法是一种基于多层次(逐轮启发式迭代)优化Modularity的算法。Modularity函数最初被用于衡量社区发现算法结果的质量,它能够刻画发现的社区的紧密程度。
同时,Modularity函数既然能刻画社区的紧密程度,也就能够被用来当作一个优化函数(目标函数),即将结点加入它的某个邻居所在的社区中,如果能够提升当前社区结构的modularity。则说明这次迭代优化是可接受的。
下面我们来讨论Louvain算法模型的核心组件。
0x1:Modularity的定义 - 描述社区内紧密程度的值Q
模块度是评估一个社区网络划分好坏的度量方法,它的物理含义是社区内节点的连边数与随机情况下的边数只差,它的取值范围是 [−1/2,1),其定义如下:
A为邻接矩阵,代表了节点 i 和节点 j 之间 边的权重,网络不是带权图时,所有边的权重可以看做是 1;
是所有与节点 i 相连的 边的权重之和(度数),kj也是同样;
表示所有边的权重之和(边的数目),充当归一化的作用;
是节点 i 的社区, 函数表示若节点 i 和节点 j 在同一个社区内,则返回 1,否则返回 0;
模块度的公式定义可以作如下简化:
其中 Σin 表示社区 C 内的边的权重之和;Σtot 表示与社区 C 内的节点相连的所有边的权重之和。
上面的公式还可以进一步简化成:
这样模块度也可以理解是:
首先modularity是针对一个社区的所有节点进行了累加计算。
modularity Q的计算公式背后体现了这种思想:社区内部边的权重减去所有与社区节点相连的边的权重和,对无向图更好理解,即社区内部边的度数减去社区内节点的总度数。
可以直观去想象一下,如果一个社区节点完全是“封闭的(即所有节点都互相内部连接,但是不和社区外部其他节点有连接,则modularity公式的计算结果为1)”
基于模块度的社区发现算法,都是以最大化模块度Q为目标。可以看到,这种模型可以支持我们通过策略优化,去不断地构造出一个内部聚集,外部稀疏连接的社区结构
在一轮迭代后,若整个 Q 没有变化,则停止迭代,否则继续迭代,直至收敛。
0x2:模块度增量 delta Q
模块增益度是评价本次迭代效果好坏的数值化指标,这是一种启发式的优化过程。类似决策树中的熵增益启发式评价。
代表由节点 i 入射集群 C 的权重之和;代表入射集群 C 的总权重;ki 代表入射节点 i 的总权重;
在算法的first phase,判断一个节点加入到哪个社区,需要找到一个delta Q最大的节点 i,具体的算法我们后面会详细讨论,这里只需要记住 delta Q的作用类似决策树中的信息增益评估的作用,它帮助整个模型向着Modularity不断增大的方向去靠拢。
3. LOUVAIN算法策略
Louvain算法是基于模块度的社区发现算法,该算法在效率和效果上都表现较好,并且能够发现层次性的社区结构,其优化目标是:最大化整个社区网络的模块度。
即让整个社区网络呈现出一种模块聚集的结构。
0x1:算法思想的联想
Louvain算法包括两个阶段,在步骤一它不断地遍历网络中的结点,尝试将单个结点加入能够使modularity提升最大的社区中,直到所有结点都不再变化。在步骤二,它处理第一阶段的结果,将一个个小的社区归并为一个超结点来重新构造网络,这时边的权重为两个结点内所有原始结点的边权重之和。迭代这两个步骤直至算法稳定。
从核心思想上来看,Louvain算法的分步迭代优化过程,和EM优化算法有异曲同工之妙。
同时有一个题外话值得注意,Louvain算法是一个迭代算法,每一轮迭代都会产出一个当前局部最优的社区结构,所以理论上,假如算法迭代了5次,我们可以得到5个不同粒度层次的社区结构,从业务场景上,这为我们发现不同的社区聚集提供了一个更灵活的视角。
关于社区发现算法可以应用在哪些领域,我也还在思考中,从这个算法的思想上来看,我倾向于认为社区发现算法比较适合发现一种"抽象泛共现模式",这种共现是一种泛化的共现,它可以是任何形式的共现,例如
社区发现可能可以提供一种更高层的视角来看待整体的大盘情况,具体的应用场景还需要不断的摸索。
0x2:关于启发式/贪婪思想的社区发现的进一步思考
社区发现算法,或者说在社区发现的项目中,很容易遇到的一个问题就是:“社区过大,将过多的outerlier包括到了社区中”,换句话说,社区聚类的过程中没有能及时收敛。
我们来看下面这张图:
如果按照启发式/贪婪思想进行”one-step one node“的社区聚类,O9、O10、O11会被先加入到社区D中,因为在每次这样的迭代中,D社区内部的紧密度(不管基于node密度还是edge得modularity评估)都是不断提高,符合算法的check条件,因此,O9、O10、O11会被加入到社区D中。
随后,O1 ~ O8也会被逐个被加入到社区D中,加入的原因和O9、O10、O11被加入是一样的。
从局部上来看,这些步骤是合理的,但是如果从上帝视角的全局来看,这种做法导致outerlier被错误的聚类到的社区中,导致precision下降。
解决这种问题的一个办法我觉得可以从CNN/DNN的做法中得到灵感,即设置一个Delta增益的阈值,即在每轮的迭代中(社区扩增后紧密度提升的度量)如果不能超过这个阈值,则判定为收敛成功,立即停止算法迭代。
0x3:社区发现(聚类)的效果非常依赖于weight权重计算的策略和方法
louvain社区发现是将一个无向权重图,转化为多个节点集合,每个节点集合代表了一个社区。
社区发现的思想是非常直接简单的,因为聚类的效果非常依赖于weight权重计算的方法,我们选择的权重计算方法必须要能够“很好地”在值域空间中分离开来,否则会导致overcluster。
举个例子,假设我们有一组节点间权重,我们按列的形式写出来:
上述社区中,我们很容易理解,应该分为两个社区:
因为这2个社区满足社区内内聚,社区间相异的社区拓朴结构特性。
反过来可以想象,如果weight权重的计算公式不够合理,社区间的关系(A-D、B-F)和各自的社区weight很接近,则pylouvain就无法很好的将两个社区区分开来了。因为在pylouvain看来,它们也属于社区的一部分。
4. LOUVAIN算法流程
0x1:算法形式化描述
1)初始化:
将图中的每个节点看成一个独立的社区,社区的数目与节点个数相同;
2)开始first phase迭代 - 社区间节点转移:
对每个节点i,依次尝试把节点 i 分配到其每个邻居节点所在的社区,计算分配前与分配后的模块度变化
3)重复2)- 继续进行社区间节点转移评估:
直到所有节点的所属社区不再变化,即社区间的节点转移结束,可以理解为本轮迭代的 Local Maximization 已达到;
4)second phase - Rebuilding Graph:
因为在这轮的first phase中,社区 C 中新增了一个新的节点 i,而 i 所在的旧的社区少了一个节点,因此需要对整个图进行一个rebuild。
对图进行重构,将所有在同一个社区的节点重构成一个新社区,社区内节点之间的边的权重更新为新节点的环的权重,社区间的边权重更新为新节点间的边权重;
5)重复2)- 继续开始下一轮的first/second phase:
直到整个图的模块度不再发生变化。
0x2:算法时间复杂度
从流程来看,该算法能够产生层次性的社区结构,其中计算耗时较多的是最底一层的社区划分,节点按社区压缩后,将大大缩小边和节点数目,
并且计算节点 i 分配到其邻居 j 时,模块度的变化只与节点 i、j 的社区有关,与其他社区无关,因此计算很快。
在论文中,把节点 i 分配到邻居节点 j 所在的社区 c 时模块度变化为:
5. A Python implementation of the Louvain method to find communities in large networks
社区发现的最后一轮结果为:
以polbooks.gml为例,16、17、18、19被归类为同一个社区:
利用louvain进行社区发现的核心在于,我们需要对我们的业务场景进行抽象,提取出node1_src/node_dst的节点概念,同时要通过专家领域经验,进行节点间weight的计算,得到了weight后,就可以通过louvain这种迭代算法进行社区拓朴的发现了。
6. 其他社区发现算法
相关问题推荐
换行。比如,print hello\nworld效果就是helloworld\n就是一个换行符。\是转义的意思,'\n'是换行,'\t'是tab,'\\'是,\ 是在编写程序中句子太长百,人为换行后加上\但print出来是一整行。...
十种常见排序算法一般分为以下几种:(1)非线性时间比较类排序:a. 交换类排序(快速排序、冒泡排序)b. 插入类排序(简单插入排序、希尔排序)c. 选择类排序(简单选择排序、堆排序)d. 归并排序(二路归并排序、多路归并排序)(2)线性时间非比较类排序:...
前景很好,中国正在产业升级,工业机器人和人工智能方面都会是强烈的热点,而且正好是在3~5年以后的时间。难度,肯定高,要求你有创新的思维能力,高数中的微积分、数列等等必须得非常好,软件编程(基础的应用最广泛的语言:C/C++)必须得很好,微电子(数字电...
迭代器与生成器的区别:(1)生成器:生成器本质上就是一个函数,它记住了上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。而且记录了程序执行的上下文。生成器不仅记住了它的数据状态,生成器还记住了程序...
python中title( )属于python中字符串函数,返回’标题化‘的字符串,就是单词的开头为大写,其余为小写
第一种解释:代码中的cnt是count的简称,一种电脑计算机内部的数学函数的名字,在Excel办公软件中计算参数列表中的数字项的个数;在数据库( sq| server或者access )中可以用来统计符合条件的数据条数。函数COUNT在计数时,将把数值型的数字计算进去;但是...
head是方法,所以需要取小括号,即dataset.head()显示的则是前5行。data[:, :-1]和data[:, -1]。另外,如果想通过位置取数据,请使用iloc,即dataset.iloc[:, :-1]和dataset.iloc[:, -1],前者表示的是取所有行,但不包括最后一列的数据,结果是个DataFrame。...
挺简单的,其实课程内容没有我们想象的那么难、像我之前同学,完全零基础,培训了半年,直接出来就工作了,人家还在北京大公司上班,一个月15k,实力老厉害了
Python针对众多的类型,提供了众多的内建函数来处理(内建是相对于导入import来说的,后面学习到包package时,将会介绍),这些内建函数功用在于其往往可对多种类型对象进行类似的操作,即多种类型对象的共有的操作;如果某种操作只对特殊的某一类对象可行,Pyt...
相当于 ... 这里不是注释
还有FIXME
python的两个库:xlrd和xlutils。 xlrd打开excel,但是打开的excel并不能直接写入数据,需要用xlutils主要是复制一份出来,实现后续的写入功能。
单行注释:Python中的单行注释一般是以#开头的,#右边的文字都会被当做解释说明的内容,不会被当做执行的程序。为了保证代码的可读性,一般会在#后面加一两个空格然后在编写解释内容。示例:# 单行注释print(hello world)注释可以放在代码上面也可以放在代...
主要是按行读取,然后就是写出判断逻辑来勘测行是否为注视行,空行,编码行其他的:import linecachefile=open('3_2.txt','r')linecount=len(file.readlines())linecache.getline('3_2.txt',linecount)这样做的过程中发现一个问题,...
或许是里面有没被注释的代码
自学的话要看个人情况,可以先在B站找一下视频看一下