拯救pandas計劃(16)——將DataFrame的奇偶列位置進行前后對調
拯救pandas計劃(16)——將DataFrame的奇偶列位置進行前后對調
最近發(fā)現周圍的很多小伙伴們都不太樂意使用pandas,轉而投向其他的數據操作庫,身為一個數據工作者,基本上是張口pandas,閉口pandas了,故而寫下此系列以讓更多的小伙伴們愛上pandas。
系列文章說明:
系列名(系列文章序號)——此次系列文章具體解決的需求
平臺:
windows 10 python 3.8 pandas >=1.2.4
/ 數據需求
首先非常感謝【瑜亮老師】在交流群里給出的問題,有了寫下這一篇的靈感。
【瑜亮老師】在群里給出的問題是:把df的奇數列與偶數列調換位置。比如A列,B列,調換成B列,A列…… 其中的數據列是偶數個,在操作方面會稍微簡單點,這里做一個拓展,使用奇數個的列進行操作。原始數據如下:
ps: 提到奇偶列為自然數從1開始計數,如A列位置為奇數列
import?pandas?as?pd
en?=?'abcdefg'
df?=?pd.DataFrame(([i?+?j?for?j?in?en]?for?i?in?en),?columns=list(en.upper()),?index=list(en.upper()))

目標樣式:

可以進行奇偶匹配的相鄰兩列發(fā)生了位置變換,而最后一列G無法與其后的數據匹配則無需交換,仍然處于最后位置。
/ 需求拆解
在文章開始就有說到,列數量為偶數比奇數容易處理,所以可以先將數據看做列數量為偶數個進行處理。將數據降低為偶數個列的數據框。
/ 需求處理
偶數個列的數據框
import?pandas?as?pd
en?=?'abcdef'
df?=?pd.DataFrame(([i?+?j?for?j?in?en]?for?i?in?en),?columns=list(en.upper()),?index=list(en.upper()))

比問題中提到的數據框少了一列G,因為是奇偶列且相鄰的兩列進行對調,可以使用numpy將提取出來的奇數列和偶數列組成2*n的二維數組,再以列方向攤平成一維數組,方法如下。
import?numpy?as?np
odd_c?=?df.columns[::2]??#?獲取奇數列
even_c?=?df.columns[1::2]??#?獲取偶數列
#?生成2*n的數組再從列方向攤平成一維數組,注意:偶數列在上方
new_c?=?np.array((even_c,?odd_c)).flatten('F')
print(df[new_c])

df.columns自身的values對象就是np.array類型的,可以直接通過自身的轉換一步步達到目的。
先將df.columns進行重新排序,原來是一維數組改成二維數組,改變過程中,以列方向進行排序,即:
new_c?=?df.columns.values.reshape((2,?len(df.columns)?//?2),?order='F')

與上一方法不同的是,奇數位置列處于上方,此時將上下位置調換就可以再使用flatten方法將數組攤平,并達到奇偶列位置調換的目的。
df[new_c[::-1].flatten('F')]

上述兩種都是將列名提取重新構造形狀再轉為一維數組形式,完成奇偶列的位置調換。
還可以運用-1的n次方的性質,如果為奇數,則使用它前一個列,如果為偶數就是用當前列的后一列,例如,[0,1] --> [1,0],在這里就很簡單的完成了交換。
new_c?=?[df.columns[i?+?(-1)?**?i]?for?i?in?range(len(df.columns))]
#?['B',?'A',?'D',?'C',?'F',?'E']
df[new_c]
此處的i為各個列名所在的索引位置,A列的索引為0,通過 0 + (-1) ** 0的算數運算得到1,即為B列的索引。而遍歷到i=1時,通過計算為1 + (-1) ** 1 = 0,B列處就返回了A列的索引。
奇數個列的數據框
以上使用了簡單的方法將偶數個列的數據框中的奇偶列位置完成的對調。
顯然,要將奇數個列名重新構造成一個2*n的數組是不行的,且得知最后一列無需跟任何一列進行位置交換,可以將最后一個列名單獨拿出,讓剩下的組成2*n的數組。
import?numpy?as?np??
c?=?df.columns.values
#?分離最后一個列名
c_?=?c[:-1]
c_last?=?c[-1]
#?方法同偶數個列數據框
c_new_even?=?c_.reshape((2,?len(c_)?//?2),?order='F')[::-1].flatten('F')
#?將最后一個列名拼接到最后
new_c?=?np.hstack([c_new_even,?c_last])
然而通用性就差了一點,不能將偶數個列的數據框進行轉換,可以試下下面這個方式:
import?numpy?as?np
c?=?df.columns.values
#?獲取另一個維度值
c_middle?=?len(c)?//?2
#?獲取列中的最后一個列名,如果為偶數列則返回空列表
c_last?=?[c[-1]]?*?(0?+?len(c)?%?2)
#?重構形狀,拼接最后一個列名
new_c?=?np.hstack((c[:c_middle?*?2].reshape((2,?c_middle),?order='F')[::-1].flatten('F'),?c_last))
這個就可以即對偶數個列進行操作又能對奇數個列進行操作,兩個不同點在于,下面這種動態(tài)獲取最后一個元素,如果列數量是偶數個,則返回空列表。
在使用(-1)的n次方時處理奇數個列也是會遇到無法將奇偶數位置進行交換。
[i?+?(-1)?**?i?for?i?in?range(len(df.columns))]

數據列名為A-G,對應的索引為0-6(包括6),使用該方法生成的7是不能從列名中提取的。此時可以使用數據修剪函數,將大于最大索引的數據修正為最大索引。
import?numpy?as?np
np.clip([i?+?(-1)?**?i?for?i?in?range(len(df.columns))],?0,?len(df.columns)?-?1)

生成的索引為該數據中最大的索引值,且此之前的列均發(fā)生了奇偶列位置對換。
/ 總結
本例中使用簡單的解決方法,通過使用列名的索引查找,numpy數組的形狀的靈活變換,數組的數據修剪,數字-1的次方性質等方法,非常簡便的將數據框的奇偶列位置進行順序改變,對此例產生發(fā)散思維,多角度解決數據需求,仍有考慮不足之處,煩請各位看官諒解。
綠葉新枝芽初開,望等閑。
于二零二二年五月二十日作
