介绍我是一名程序员。我从大学时代就开始编程了,我仍然对用简单的Python代码可以打开多少路径感到惊讶。但我并不总是那么有效率。我相信这是大多数程序员(尤其是新手程序员)的共同特征,编写代码的快感总是优先于效率和简单性。虽然这在我们大学期间有效,但在专业环境中,尤其是在数据科学项目中,情况却大不相同。作为数据科学家,编写优化的Python代码非常非常重要。混乱、低效的代码是在浪费你的时间,甚至是你项目的金钱。经验丰富的数据科学家和专业人士知道,当我们与客户合作时,混乱的代码是不可接受的。因此,在本文中,我将利用我多年的编程经验,列出并演示四种可用于优化数据科学项目中的Python代码的方法。什么是优化?首先定义什么是优化。我们将使用一个直观的例子来做到这一点。这是我们的问题:假设给定一个数组,其中每个索引代表一个城市,该索引处的值代表该城市与下一个城市之间的距离。假设我们有两个索引,我们需要计算这两个索引之间的总距离。简单来说,我们需要找到两个给定索引之间的距离之和。首先想到的是,一个简单的FOR循环在这里就可以正常工作。但是如果有超过100,000个城市并且我们每秒收到超过50,000个查询怎么办?您仍然认为FOR循环可以为我们的问题提供足够好的解决方案吗?FOR循环没有提供足够好的解决方案。这就是优化的用武之地。简而言之,代码优化意味着减少执行任何任务所需的操作数量,同时产生正确的结果。让我们计算FOR循环执行此任务所需的操作次数:我们必须在上述数组中找到索引1和索引3处的城市之间的距离。循环对于小数组大小执行良好如果数组大小为100,000且查询数为50,000怎么办?这是很多数字。如果数组的大小和查询的数量进一步增加,我们的FOR循环将花费大量时间。你能想出一种优化方法,使我们在使用较少数量的解决方案时也能产生正确的结果吗?这里我将讨论一个更好的方案来解决这个问题,即使用前缀数组来计算距离问题。让我们看看它是如何工作的:你能理解吗?我们在一次操作中得到相同的距离!这种方法最好的地方在于,索引之间的差值是1还是100,000并不重要,只计算一次操作中任意两个索引之间的距离。我创建了一个数组大小为100,000和50,000个查询的示例数据集。可以自己执行代码比较两者的耗时注意:数据集一共有50000个query,你可以改变参数execute_queries最多执行50000个query,看看每个方法执行任务需要多长时间。importtimefromtqdmimporttqdmdata_file=open('sample-data.txt','r')distance_between_city=data_file.readline().split()queries=data_file.readlines()print('SIZEOFARRAY=',len(distance_between_city))print('TOTALNUMBEROFQUERIES=',len(queries))data_file.close()#分配执行查询的次数execute_queries=2000print('\n\nExecuting',execute_queries,'Queries')#FOR循环方式#读取文件并存储距离并查询start_time_for_loop=time.time()data_file=open('sample-data.txt','r')distance_between_city=data_file.readline().split()queries=data_file.readlines()#存储距离列表distances_for_loop=[]#函数计算起始索引和结束索引之间的距离defcalculateDistance(startIndex,endIndex):distance=0fornumberinrange(startIndex,endIndex+1,1):distance+=int(distance_between_city[number])returndistanceforqueryintqdm(queries[:execute_queries]):query=query.split()startIndex=int(query[0])endIndex=int(query[1])distances_for_loop.append(calculateDistance(startIndex,endIndex))data_file.close()#获取结束时间end_time_for_loop=time。time()print('\n\nTimeTakentoexecutetaskbyforloop:',(end_time_for_loop-start_time_for_loop),'seconds')#前面数据组方法#读取文件并存储距离和查询start_time_for_prefix=time.time()data_file=open('sample-data.txt','r')distance_between_city=data_file.readline().split()queries=data_file.readlines()#存储距离列表distances_for_prefix_array=[]#创建前数组prefix_array=[]prefix_array.append(int(distance_between_city[0]))foriinrange(1,100000,1):prefix_array.append((int(distance_between_city[i])+prefix_array[i-1]))forqueryintqdm(queries[:execute_queries]):query=query.split()startIndex=int(查询[0])endIndex=int(query[1])ifstartIndex==0:distances_for_prefix_array.append(prefix_array[endIndex])else:distances_for_prefix_array.append((prefix_array[endIndex]-prefix_array[startIndex-1]))data_file.close()end_time_for_prefix=time.time()print('\n\nTimeTakenbyPrefixArraytoexecutetaskis:',(end_time_for_prefix-start_time_for_prefix),'seconds')#检查结果correct=Trueforresultinrange(0,execute_queries):ifdistances_for_loop[result]!=distances_for_prefix_array[result]:correct=Falseifcorrect:print('\n\nDistancecalculatedbyboththemethodsmatched.')else:print('\n\nResultsdidnotmatched!!')结果节省了大量时间,这就是优化Python代码的重要性。我们不仅节省了时间,还节省了大量的计算资源!您可能想知道这些如何应用于数据科学项目。您可能已经注意到,很多时候我们必须对大量数据点执行相同的查询。在数据预处理阶段尤其如此。我们必须使用一些优化的技术而不是基本的编程来尽可能快速高效地完成工作。所以,在这里我将分享一些我用来改进和优化我的Python代码的好技巧1.Pandas.apply()|特征工程Pandas的钻石级功能已经是一个高度优化的库,但我们大多数人仍然没有充分利用它。现在你想想你会在数据科学中使用它的常见地方。我能想到的一个是特征工程,我们使用现有特征来创建新特征。最有效的方法之一是使用Pandas.apply()。在这里我们可以传递一个用户定义的函数并将其应用于Pandas序列化数据的每个数据点。它是Pandas库中的优秀插件之一,因为该功能可以根据所需标准选择性地隔离数据。因此,我们可以有效地将其用于数据处理任务。让我们使用Twitter情绪分析数据来计算每条推文的字数。我们将使用不同的方法,例如数据框iterrows方法、NumPy数组和应用方法。您可以从此处下载数据集(https://datahack.analyticsvidhya.com/contest/practice-problem-twitter-sentiment-analysis/?utm_source=blog&utm_medium=4-methods-optimize-python-code-data-science)。'''优化方法:apply方法'''#导入库importpandasaspdimportnumpyasnpimporttimeimportmathdata=pd.read_csv('train_E6oV3lV.csv')#打印头信息print(data.head())#使用dataframeiterows计算字符数print('\n\n使用Iterrows\n\n')start_time=time.time()data_1=data.copy()n_words=[]fori,rowindata_1.iterrows():n_words.append(len(row['tweet'].split()))data_1['n_words']=n_wordsprint(data_1[['id','n_words']].head())end_time=time.time()print('\nTimetaketocalculateNo.ofWordsbyiterrows:',(end_time-start_time),'seconds')#使用Numpy数组计算字符数print('\n\nUsingNumpyArrays\n\n')start_time=time.time()data_2=data.copy()n_words_2=[]forrowindata_2.值:n_words_2.append(len(row[2].split()))data_2['n_words']=n_words_2print(data_2[['id','n_words']].head())end_time=time.time()print('\nTimetaketocalculateNo.ofWordsbynumpyarray:',(end_time-start_time),'seconds')#使用apply方法计算字符数print('\n\nUsingApplyMethod\n\n')start_time=time.time()data_3=data.copy()data_3['n_words']=data_3['tweet'].apply(lambdax:len(x.split()))原则t(data_3[['id','n_words']].head())end_time=time.time()print('\nTimetaketocalculateNo.ofWordsbyApplyMethod:',(end_time-start_time),'seconds')你可能已经注意到了apply方法比iterrows方法快得多,其性能可与NumPy数组相媲美,但apply方法提供了更大的灵活性。您可以在此处阅读apply方法的文档。(https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.apply.html)2.Pandas.DataFrame.loc|Python数据处理技巧这是我最喜欢的Pandas库的技巧之一。我觉得这是处理数据任务的数据科学家必须知道的方法(所以几乎每个人!)。大多数时候,我们只需要根据某些条件更新数据集中特定列的某些值。Pandas.DataFrame.loc为我们提供了针对此类问题的优化方案。让我们使用loc函数解决一个问题。您可以在此处下载您将使用的数据集(https://drive.google.com/file/d/1VwXDA27zgx5jIq8C7NQW0A5rtE95e3XI/view?usp=sharing)。#Importlibraryimportpandasasspddata=pd.read_csv('school.csv')data.head()检查“City”变量每个值的出现频率:现在,假设我们只需要前5个城市,想替换其余为“Others”(其他)城市。所以,让我们这样写:#将热门城市保存在列表中City.isin(top_cities)==False),'City']='Others'#每个城市的频次data.City.value_counts()Pandas更新数据的值非常方便!这就是解决此类数据处理任务的优化方法。3.在Python中向量化你的函数另一种摆脱慢循环的方法是向量化你的函数。这意味着新创建的函数将应用于输入列表并返回结果数组。Python中的矢量化可以加速计算让我们在同一个Twitter情绪分析数据集上验证这一点。'''优化方法:向量化函数'''#导入库importpandasaspdimportnumpyasnpimporttimeimportmathdata=pd.read_csv('train_E6oV3lV.csv')#输出头信息print(data.head())defword_count(x):returnlen(x.split())#使用Dataframeiterrows计算字数print('\n\nUsingIterrows\n\n')start_time=time.time()data_1=data.copy()n_words=[]fori,rowindata_1.iterrows():n_words.append(word_count(row['tweet']))data_1['n_words']=n_wordsprint(data_1[['id','n_words']].head())end_time=time.time()print('\nTimetaketocalculateNo.ofWordsbyiterrows:',(end_time-start_time),'seconds')#使用向量化方法计算字数print('\n\nUsingFunctionVectorization\n\n')start_time=time.time()data_2=data.copy()#向量化函数vec_word_count=np.vectorize(word_count)n_words_2=vec_word_count(data_2['tweet'])data_2['n_words']=n_words_2print(data_2[['id','n_words']].head())end_time=time.time()print('\nTimetaketocalculateNo.ofWordsbynumpyarray:',(end_time-start_time),'seconds')难以置信,不是吗?对于上面的示例,矢量化速度提高了80倍!不仅有助于加快我们的代码速度并使其更清晰。4.Python中的多进程多进程是系统同时支持多个处理器的能力。这里我们将流程拆分为多个任务,所有任务独立运行。当我们处理大型数据集时,即使是apply函数也可能看起来很慢。因此,让我们看看如何利用Python中的多处理库来加快处理速度。我们将随机创建一百万个值并为每个值找到除数。我们将使用应用函数和多进程方法来比较其性能:#导入库importpandasasspdimportmathimportmultiprocessingasmpfromrandomimportrandint#计算除数的函数defcountDivisors(n):count=0foriinrange(1,(int)(math.sqrt(n))+1):if(n%i==0):%%timepool=mp.Pool(processes=(mp.cpu_count()-1))answer=pool.map(countDivisors,random_data)pool.close()pool.join()if(n/i==i):count=count+1else:count=count+2returncount#创建随机数random_data=[randint(10,1000)foriinrange(1,1000001)]data=pd.DataFrame({'Number':random_data})data.shape%%timedata['Number_of_divisor']=data.Number.apply(countDivisors)%%timepool=mp.Pool(processes=(mp.cpu_count()-1))answer=pool.map(countDivisors,random_data)pool.close()pool.join()在这里,多处理比应用方法快13倍。性能可能因不同的硬件系统而异,但肯定会提高性能。结束这绝不是一个详尽的清单。还有许多其他方法和技术可以优化Python代码。但是我在我的数据科学职业生涯中发现并经常使用这四个,我相信你也会发现它们很有用。
