Python初學總整理 第9講:Numpy函式庫

嗨,大家好,我是Teresa,這集想來和大家介紹在資料分析、機器學習領域中很經典、必定會用到的函式庫-Numpy,目的主要是做數據的處理,但內容不會涉及統計的理論和延伸,只會做一些基礎的功能介紹和簡易的實作程式碼提供給大家。

什麼是Numpy?

Numpy是一個免費且開源的Python函式庫,專門用來處理陣列,由Travis Oliphant於2005年創建,但Numpy的前身是Numeric,是由Jim Hugunin和其他協作者共同開發,Numpy結合另一個同性質程式庫Numarray的特色,並加入了其他擴充功能。原始碼在這個網站中:https://github.com/numpy/numpy

為什麼使用Numpy?

En kral yeşilçam porno videoları izlemek için saplamaca’yı ziyaret edin. En güzel yeşilçam pornoları bu sitede

在Python中,我們也有list來存放清單,但其實處理速度很慢,Numpy處理陣列的速度比list快50倍。原因是Numpy會將資料儲存在記憶體中的一個連續位置,可以高效率的找到他們的位置、並使用資料。

安裝Numpy

pip install numpy

import numpy

#測試程式碼
arr = numpy.array([1, 2, 3, 4, 5])

print(arr)
#如果覺得每次都要輸入numpy很麻煩,在匯入的時候可以輸入:

import numpy as np

arr = np.array([1, 2, 3, 4, 5])

建立陣列

Numpy的陣列稱為ndarray,創建陣列的函數為:ndarray array( )

arr0 = np.array(40)

print(arr0) #只有一個元素的陣列

arr1 = np, array([1, 2, 3, 4, 5])

print(arr1) #稱為單維或一維的陣列,裡面的值包含1, 2, 3, 4, 5

arr2 = np.array([[1, 2, 3], [4, 5, 6]])

print(arr2) #包含一個以上的一維陣列稱為二維陣列,可用來表示矩陣

arr3 = np. array([[[1, 2, 3], [4, 5, 6]], [[ 1, 2, 3], [4, 5, 6]]])

print(arr3) #包含一個以上的二維陣列稱為三維陣列

利用函數ndim 可以得知陣列有幾個維度

arr0 = np.array(40)

arr1 = np, array([1, 2, 3, 4, 5])

arr2 = np.array([[1, 2, 3], [4, 5, 6]])

arr3 = np. array([[[1, 2, 3], [4, 5, 6]], [[ 1, 2, 3], [4, 5, 6]]])

print(arr0.ndim) #0

print(arr1.ndim) #1

print(arr2.ndim) #2

print(arr3.ndim) #3

陣列可以有任何數的維度,可以用ndmin來定義維度的數量

arr = np.array([1, 2, 3, 4], admin = 5)

print(arr)

print(“此陣列為", arr.ndim, “維度")

陣列的大小

利用shape可以得到陣列是以幾個維度、幾個元素所組成

arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

print(arr.shape) #(2, 4),兩個維度,每個維度有4個元素

重塑陣列

利用reshape可以改變陣列的大小

只要陣列大小中的元素相等,我們可以將陣列重塑成各種形狀

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])

newarr = arr.reshape(4, 3)  #4個陣列,每個陣列有3個元素

print(newarr)  #[[ 1  2  3] [ 4  5  6] [ 7  8  9] [10 11 12]]

newarr1 = arr.reshape(2, 3, 2) #2個維度,每個維度2個陣列,每個陣列2個元素

print(newarr1)  #[[[ 1  2] [ 3  4] [ 5  6]]  [[ 7  8] [ 9 10] [11 12]]]

假設陣列中的元素很多,可以只指定要重塑成幾維度和幾個陣列,元素個數可用-1取代

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])

newarr = arr.reshape(2, 2, -1)

print(newarr)  #[[[1 2] [3 4]] [[5 6] [7 8]]]

如果想要把陣列展開,可以利用reshape(-1)

arr = np.array([[1, 2, 3], [4, 5, 6]])

newarr = arr.reshape(-1)

print(newarr)  #[1 2 3 4 5 6]

索引陣列

如果還記得我曾在字串中提到索引的概念,也同樣可以套用在這裡

再次提醒,索引值是從0開始

arr = np.array([1, 2, 3, 4])

print(arr[0]) #1

print(arr[0]) #2

print(arr[2] + print(arr[3])) #7,取得陣列中第3和第4個元素並相加

如果想要索引二維或三維陣列中的元素,可以用逗號分隔 [ 陣列, 元素]

arr2 = np.array([[1,2,3,4,5], [6,7,8,9,10]])

print("第一個陣列的第二個元素是: ", arr2[0, 1]) #2

print("第二個陣列的第五個元素是: ", arr2[1, 4]) #10

索引三維陣列:[第幾維度, 陣列, 元素]

arr3 = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])

print(arr3[0, 1, 2]) #6

負索引:-1 表示從最後一個元素開始

arr2 = np.array([[1,2,3,4,5], [6,7,8,9,10]])

print("第一個陣列的第二個元素是: ", arr2[1, -1]) #10

切片陣列

切片可以把陣列從某個元素索引到某個元素,[開始:結束:步數]

注意:切片的結果會包含開始,不包含結束

arr = np.array([1, 2, 3, 4, 5, 6, 7])

print(arr[1:5])  #步數預設為1,切片結果:[2,3, 4, 5]

print(arr[4:])  #從第5個元素,切到最後一個元素,結果:[5, 6, 7]

print(arr[:4])  #從第一個元素,切到第4-1個元素,結果:[1, 2, 3, 4]

print(arr[-3:-1]) #運用負索引的概念,結果:[5, 6]

print(arr[1:5:2]) #結果:[2, 4]

print(arr[::2])  #結果:[1, 3, 5, 7]

如果是要切片二維陣列 [ 陣列,開始:結束:步數]

arr = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])

print(arr[1, 1:4]) #[7, 8, 9]

複製與檢視陣列

複製(copy)陣列後,對複本有所更改都不會影響原始陣列;相反地,對原始陣列做任何更改也不會影響複本。

檢視(view)陣列的話,對陣列有所更改,將會影響到原始陣列的數據,需特別注意。

arr = np.array([1, 2, 3, 4, 5])

x = arr.copy()

arr[0] = 42

print(arr) #[42, 2, 3, 4, 5]

print(x)  #[1, 2, 3, 4, 5],對複本沒有影響
arr = np.array([1, 2, 3, 4, 5])

x = arr.view()

x[0] = 31

print(arr) #[31, 2, 3, 4, 5]

print(x)  #[31, 2, 3, 4, 5],改變原始陣列也改變檢視中的陣列

要如何判斷陣列是不是會被改變? 利用函數 base

arr = np.array([1, 2, 3, 4, 5])

x = arr.copy()

y = arr.view()

print(x.base)  #None,表示屬於一個新的陣列,不會改變原始陣列

print(y.base)  #[1, 2, 3, 4, 5],表示數據來自此陣列,修改後原始陣列也會受影響

遍歷陣列

如果要運用到陣列中的每一個元素,以for迴圈來寫,會讓程式碼變得非常複雜

我們可以使用nditer( )來達到同樣的效果

arr = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])

for x in np.nditer(arr):

    print(x)  #1 2 3 4 5 6 7 8

for y in np.nditer(arr[:, ::2]):  #運用索引的概念

    print(y)  #1, 3, 5, 7

使用ndenumerate( )可以讓輸出的結果包含元素的索引號

arr = np.array([1, 2, 3])

for idx, x in np.ndenumerate(arr):

    print(idx, x)  #(0,) 1    (1,) 2     (2,) 3

arr1 = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

for idx, y in np.ndenumerate(arr1):

    print(idx, y)  #(0, 0) 1     (0, 1) 2     (0, 2) 3     (0, 3) 4

#(1, 0) 5     (1, 1) 6     (1, 2) 7     (1, 3) 8

串聯陣列

利用concatenate(陣列,陣列,軸)可以把兩個或多個陣列放在同一個陣列中,軸預設值為0

arr1 = np.array([1, 2, 3])

arr2 = np.array([4, 5, 6])

arr = np.concatenate((arr1, arr2))

print(arr)  #[1 2 3 4 5 6]

arr3 = np.array([[1, 2], [3, 4]])

arr4 = np.array([[5, 6], [7, 8]])

arr = np.concatenate((arr3, arr4), axis=1)  #軸= 1表示二維陣列

print(arr)  #[[1 2 5 6] [3 4 7 8]]

利用stack( )可以沿著所指定的軸將值堆疊上去

hstack( ):沿著行堆疊

vstack( ):沿著列堆疊

dstack( ):沿著高度堆疊

arr1 = np.array([1, 2, 3])

arr2 = np.array([4, 5, 6])

arr = np.stack((arr1, arr2), axis=1)

print(arr)  #[[1 4] [2 5] [3 6]]

arr = np.hstack((arr1, arr2))

print(arr)  #[1 2 3 4 5 6]

arr = np.vstack((arr1, arr2))

print(arr)  #[[1 2 3] [4 5 6]]

arr = np.dstack((arr1, arr2))

print(arr)  #[[[1 4] [2 5] [3 6]]]

分割陣列

分割是串聯的反向操作,利用array_split( )可以將一個陣列分割成多個陣列,一二三維度同理

arr = np.array([1, 2, 3, 4, 5, 6])

newarr = np.array_split(arr, 3)

print(newarr)

#[array([1, 2]), array([3, 4]), array([5, 6])]

print(newarr[0]) #[1, 2]

print(newarr[1]) #[3, 4]

print(newarr[2]) #[5, 6]

陣列排序

利用sort( )可將陣列排序,會生成複製的陣列,不會影響到原始的陣列

arr = np.array([3, 2, 0, 1])

print(np.sort(arr)) #[0, 1, 2, 3]

arr1 = np.array([[3, 2, 4], [5, 0, 1]])

print(np.sort(arr1)) #[[2 3 4]  [0 1 5]]

arr2 = np.array(['banana', 'cherry', 'apple'])

print(np.sort(arr2)) #['apple' 'banana' 'cherry'],依照ASCII碼大小排列

arr3 = np.array([True, False, True])

print(np.sort(arr3)) #[False True True],依照ASCII碼大小排列

搜尋陣列

利用where( )可以尋找陣列中的某個值並回傳相對應元素的索引值

arr = np.array([1, 2, 3, 4, 5, 4, 4])

x = np.where(arr == 4)

print(x)  #(array([3, 5, 6],)

arr1 = np.array([1, 2, 3, 4, 5, 6, 7, 8])

x = np.where(arr1%2 == 0)  #可用來尋找符合條件的元素

print(x)  #(array([1, 3, 5, 7]),)

利用searchsorted()可以回傳某個值應該要放在該陣列的第幾個才能符合已排序的陣列

默認值是從左側搜尋,加入side=’right’ 可以從右側搜尋

也可以放入多個值,只要以逗號分隔即可

arr = np.array([6, 7, 8, 9])

x = np.searchsorted(arr, 10)  #想在陣列中放入10

print(x)  #4,索引值為4才可以保持陣列的順序

篩選陣列

輸入True或False可以從原有的陣列中取得元素來建立新陣列

arr = np.array([41, 42, 43, 44])

x = [True, False, True, False]

newarr = arr[x]

print(newarr)  #[41, 43]

假如陣列的元素數量多的時候,可以用for迴圈來寫篩選條件

arr = np.array([41, 42, 43, 44])

filter_arr = [ ]  #建立空的陣列

for element in arr:

    if element > 42:
        
        filter_arr.append(True)

    else:

        filter_arr.append(False)

newarr = arr[filter_arr]

print(filter_arr)  #[False, False, True, True]

print(newarr)  #[43 44]

好的,Ep. 9的整理就到這裡了,還有很多是和統計學有關的應用,就不在這裡贅述了。另外,我想你們也會對Python的資料分析套件Pandas感到興趣,來看看吧!

Python初學總整理 全系列:

Python初學總整理 第1講:Python簡介

Python初學總整理 第2講:Python開發環境

Python初學總整理 第3講:Python資料型態和運算子

Python初學總整理 第4講:Python條件、迴圈與函數

Python初學總整理 第5講:爬蟲應用(上)

Python初學總整理 第6講:爬蟲應用(下)

Python初學總整理 第7講:爬蟲實例解析 – 以爬取臉書社團為案例,使用 Selenium 來進行網頁模擬爬蟲

Python初學總整理 第8講:Matplotlib套件

Python初學總整理 第9講:Numpy函式庫 (本篇)


文章看完還是不知道該從哪裡下手?

就從線上課程開始吧!不讓你獨自摸索好幾個月,用8小時帶你走完基礎與精華,培養你基礎的Python概念,讓自學下一步不是煩惱!

全新Python 課程上架,8小時基礎實戰!,限時優惠只要NT 600 (HDK 120 起)!

不讓妳浪費一整天,只要你8小時,就能讓你學會基礎!

如果你的入門還在單打獨鬥,歡迎來到快樂學程式找到志同道合的夥伴,你的自學之路不孤單。

參考資料

  1. https://www.w3schools.com/python/numpy_intro.asp
  2. https://zh.wikipedia.org/wiki/NumPy
  3. https://blog.techbridge.cc/2020/08/24/numpy-zen-intro-tutorial/
Teresa

Recent Posts

三個你不能錯過的教學資訊

疫情之下,原本實體活動全都改成線上,活動分享全都在網路上很容易錯過,這邊整理了幾個跟數位教學相關的活動資訊給大家參考,分享順序為活動時間⏰ 1. Google Workspace技術整合術 Google Workspace for Education (原 G Suite 教育版) 能透過 Gmail、Google Drive、Google Calender、Google Meet 和 Classroom 等通訊與協作應用程式創造輕鬆與順暢的學習環境,並促進協同合作以提升數位學習與教學成效,而且還會為學校資料提供雲端安全性防護…

3 years ago

線上教學沒有臨場感?Gather來幫你解決

實體的教室變成一格一格的畫面,原本吵雜的下課時間也變成掛斷通話的系統聲。原本在班級中的歸屬感慢慢消失了,久而久之,孩子的對於學習,不再有群體的感覺,只是孤軍一人學習、複習、考試,甚至是畢業典禮都少了一份臨場感。因此,今天要和大家介紹一款現在在教育界還很少被提到的軟體-Gather。 Gather 如果你還不知道Gather,或是直覺的把Gather與虛擬會議室連結在一起,不妨先看看Gather裡的特色: 介面是像素型RPG 每個人自由創建一個角色 上下左右輕易控制角色 走道其他角色旁邊能互相討論 小遊戲battle 光是這五點特色,就足以推派它成為遠距教學的工具,除了打造不同空間的地圖外,最特別的是Gahter打造前所未有的臨場感,人與人之間只有靠近到一定距離,才能聽到彼此說話聲音,而相對應的,離開後聲音就會漸漸聽不到,就連白噪音的設計也是如此,非常的逼真。 不知道老師們會不會因為搭建地圖覺得麻煩,或是看到2D介面怕學生當遊戲在玩而不考慮這款軟體,換個角度思考,藉由不同地區的老師們集思廣益,打造出最適合學生學習的環境,像是不同學科的教室、戶外自然生態區、操場、籃球場等等....都可以更貼近真實校園,而像素的介面正好提高學生的學習意願,老師們擔心學生過於沉迷時,也能用全體廣播放上課鐘聲,或是讓學生們聚集在同一區,鏡頭站起來動一動 (另外推薦Active Arcade -> 趣味運動的APP ),就像早操一樣很真實。 線上教學缺乏臨場感,Gather可以創造了讓大家「一起在一個遊戲世界」的凝聚力。縱使Google Meet、Teams 雖然也可以進行討論,但若要每堂課要穿梭在不同會議間,節奏不流暢,使用Gather可以讓學生更自由地移動,相對應的老師也可以神出鬼沒地到處「旁聽」。 教孩子用科技解決問題,如果老師們也努力著示範給孩子看,創造出獨一無二的學習空間,相信對於孩子學習的歷程中,有莫大收穫。但到底怎麼開始使用?別擔心,這次快樂學程式邀請到Gahter界的大神 阿岳,要來和大家分享Gather的6大應用技巧,限名額唷~…

3 years ago

HTML入門系列:基本觀念介紹!

HTML入門&基本觀念介紹!         構成一個網頁,最重要的就是他的結構,而HTML就像是他的骨架,而CSS就是像是我們身體上的肌肉一般,而JS則像是人體的神經、血管般調整著我們身體、傳遞訊息,那麼HTML究竟是指哪些語法呢?   什麼是HTML?       HTML全文又稱為HyperText Markup language ,也就是所謂的超文本標記語言,是網頁構成的基本要素,換言之,網頁就是由一堆html所構成,透過瀏覽器,顯示文字、圖片、以及其他相關我們可以在網頁上看見的基本元素。而對於剛學程式的朋友來說,HTML有以下幾個重點: HTML的基本架構: HTML的基本觀念與優點1.容易學習-HTML的文檔製作非常簡單易懂﹐功能強大之餘還支持不同格式的文件鑲入。2.製作門檻低-HTML是文本﹐它需要瀏覽器的解釋。只要你學會了HTML﹐你就可以直接在Windows的記事本或寫字版上進行製作和編輯﹐當然你也可以用WPS來編寫﹐只要注意在存檔的時候用.htm或.html來做檔名就可以了3.有利於搜尋引擎理解你的內容,透過HTML所構成的頁面被稱作所謂的”靜態頁面”﹐而Google爬蟲會優先收錄靜態網頁﹐所以HTML對於Google來說就像是鯊魚聞到血腥味一樣﹐有利於吸引Google爬蟲。4.加快瀏覽速度-因為靜態網頁無需連接數據庫﹐因此比打開動態網頁的速度較快﹐對於消費者體驗來說有所幫助。5.網站更安全-因為HTML頁面不會受Asp相關漏洞所影響。 HTML的缺點與限制 1.太簡單﹐不能適應現在越來越發達的網路世界和應用的需要﹐比如手機﹑PDA﹑信息家電等都不能直接顯示HTML2.太龐大﹐由於HTML代碼不規范﹑臃腫﹐瀏覽器需要足夠智能和龐大才能夠正確顯示HTML。顯然在你的PDA上裝一個IE6是不可能的。空間不夠﹐運算也跟不上3.數據與表現混雜。這樣你的頁面要改變顯示﹐就必須重新制作HTML。對不同的網路設備顯示同樣的數據都需要制作不同的HTML4.只能對文本進行排版﹐而且HTML樣式使用標準文本標識﹐不能創建一些特殊效果 所以我該如何學習HTML? 有什麼比較快的方法嗎?       答案是沒有的﹐學習程式語言就像是學習廚藝一樣﹐只有透過不停的嘗試﹑嘗試和嘗試才能讓自己有所成長﹐並且慢慢強大。過程中你會一直遇上困難和失敗﹐但你在不斷解決困難的過程中能夠不斷發掘新的知識﹐從失敗中發才能不停的成長。無論你是學習那一種程式語言﹐都需要經歷困難和失敗才能讓自己更加強大﹐正所謂:不經一番寒徹骨,怎得梅花撲鼻香呢?對吧﹐我們能做的不是要找捷徑﹐而是透過不同的媒介和平台去不斷學習﹐那麼下面快樂學程式會為大家介紹一些很棒的程式教學網站﹐讓大家可以不斷提升自己。 1.w3schools.com- 語法練習的好地方這個網站是目前全球訪問量最大的網頁開發教程網站﹐網站裡有多種程式語言的教學﹐而且每種程式語言由淺到深的解說﹐從語言的介紹到不同的功能和方法都會分章節說明。所以無論你是從零開始學的白紙﹐還是已經在學習但遇到困難的新手都可以在這個網站上找到相關資訊﹐是個非常不錯的網站。那麼網址我們當然要雙手奉上﹐請慢用https://www.w3schools.com/ 2.CodeAcademy- 免費的教學網站這是全球其中一個最受歡迎的免費coding教學網站﹐已經有超過2400萬人透過這個網站學習到了如何去coding。這個網站是非常適合新手入門的同學去學習的﹐網站會一步一步仔細的教導而且還有實作﹐所以學習起來非常的快速! 3.StackOverflow- 實際案例與疑難雜症的解決處  這個網站比較適合一些已經有在嘗試動手實作的同學﹐當你在coding的時候遇上困難了﹐那麼你可以在這個可以解決超過80種程式語言問題的網站上找到答案﹐讓你的coding之路更加順暢。  如果你覺得爬網站麻煩又費時,不妨直接點擊快樂學程式的網頁前端課程,老師直接手把手帶你入門HTML !完整的基礎入門課程省下你獨自摸索花費的時間!   課程上架Udemy ! 準備一個輕鬆的週末,只要一天的時間,帶你建置靜態網頁。從實作中打開靜態網頁的大門,讓你的研究之路不是只有自己,有我們跟你一起努力!課程中你可以瞭解網站建置的世界觀與網站版型掌握HTML5的使用方式掌握 CSS3的使用方式使用Bootstrap處理前端UI框架現在開始上課!如果你的入門還在單打獨鬥,歡迎來到快樂學程式找到志同道合的夥伴,你的自學之路不孤單。快樂學程式

3 years ago

HTML語法整理! 3分鐘快速弄懂常用語法!

什麼是HTML?         HTML全名是HyperText Markup Language,是一種描述超文件的註記語言SGML(Standard Generalized Markup Language)所制訂出的一種網頁語言,是編寫網頁的基本語言,基本上現行的瀏覽器都可以讀取HTML,使用HTML可以編輯設計出網頁,也可以在網頁中加入所有HTML語言可支援的方式,例如表格、表單、圖片、文字、連結、程式等等。 HTML介紹與基本語法整理   不管你是小時候從撰寫無名小站為了要修改你的樣式,而開始瞭解HTML和CSS為何物,或是長大因為介面設計或前端工程開始踏入網頁的世界。HTML和CSS對於網站的重要性經過多年依然歷久不衰。在本篇會對HTML進行基礎的介紹並幫你整理出基本語法提供你在寫網頁時的快速參考!   編寫基本的HTML: 先讓大家看一下一個基本的HTML文件格式為: <HTML> <HEAD> <TITLE>網頁主題</TITLE> <Meta> </HEAD> <BODY>…

3 years ago

PHP是什麼?3分鐘PHP基本介紹!

PHP是什麼?3分鐘PHP基本介紹!       大家安安﹐快樂學程式這一次要跟大家分享PHP這種程式語言。希望幫助對於PHP有興趣的新手們可以透過我們的文章對這種語言有初步認識。一如以往﹐我們會分享以下幾項有關PHP的知識。 PHP是什麼?       PHP語言的全名是(PHP: Hypertext Preprocessor),和ASP、JSP等都是動態網頁開發語言,不過,PHP擁有跨平台的能力,無論是在Linux(最適合)、Unix、 Windows都可以執行運作,不像微軟 的ASP只能在Windows平台上執行,而且PHP是免費的,並可結合多種資料庫伺服器,如:MySQL、PostgreSQL、dBase、mSQL、Informix、ODBC、Oracle等。      PHP語言是伺服器端(Server)執行的網頁,不像一般HTML網頁,只要單機下開啟檔案就可以檢視網頁,PHP必須先在伺服器端執行完後,再將結果傳至使用者端(Client)的瀏覽器中檢視結果,所以必須使用網站伺 服器,且伺服器要支援PHP。 如何學好PHP?       要學好PHP,要有目的,要有一個想寫的東西,寫個學校網站,寫個校友系統,寫個簡單的新聞區或相簿...等,都可以,盡可能的和工作和生活結合,利用程式來簡化繁瑣的人工步驟,或者提昇工作效率,有目標,才會有動力,才會有想法。      學PHP不需要背,背不完的,只要懂就好了。像函數就不用背,常用的打久了你就背起來了,不常用的,等到要用時,知道去哪裡找就好。換言之,函數懂越多,功力越高強。      程式碼可以複製貼上,但一定要知道為什麼要這麼寫,不要傻傻的照著打,可以的話,自己打一遍最好,最上乘就是可以說出每一行程式碼的作用和前因後果。或者,故意打錯,看看會怎樣。錯誤訊息看久了,功力也就提昇了。 PHP程式碼執行方式 透過 Web Server 方式:例如利用 Apache…

3 years ago

PHP是什麼?基本介紹與語法整理

Sildenafil citrate oral jelly Combiné avec les données de départs 2, on leur a demandé de type 5 de tadalafil…

3 years ago