簡體   English   中英

如何在 Pandas 數據框中拆分一列元組?

[英]How can I split a column of tuples in a Pandas dataframe?

我有一個 Pandas 數據框(這只是一小部分)

>>> d1
   y norm test  y norm train  len(y_train)  len(y_test)  \
0    64.904368    116.151232          1645          549
1    70.852681    112.639876          1645          549

                                    SVR RBF  \
0   (35.652207342877873, 22.95533537448393)
1  (39.563683797747622, 27.382483096332511)

                                        LCV  \
0  (19.365430594452338, 13.880062435173587)
1  (19.099614489458364, 14.018867136617146)

                                   RIDGE CV  \
0  (4.2907610988480362, 12.416745648065584)
1    (4.18864306788194, 12.980833914392477)

                                         RF  \
0   (9.9484841581029428, 16.46902345373697)
1  (10.139848213735391, 16.282141345406522)

                                           GB  \
0  (0.012816232716538605, 15.950164822266007)
1  (0.012814519804493328, 15.305745202851712)

                                             ET DATA
0  (0.00034337162272515505, 16.284800366214057)  j2m
1  (0.00024811554516431878, 15.556506191784194)  j2m
>>>

我想拆分所有包含元組的列。 例如,我想用LCV-aLCV-b列替換LCV列。

我怎樣才能做到這一點?

您可以通過在該列上執行pd.DataFrame(col.tolist())來做到這一點:

In [2]: df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]})

In [3]: df
Out[3]:
   a       b
0  1  (1, 2)
1  2  (3, 4)

In [4]: df['b'].tolist()
Out[4]: [(1, 2), (3, 4)]

In [5]: pd.DataFrame(df['b'].tolist(), index=df.index)
Out[5]:
   0  1
0  1  2
1  3  4

In [6]: df[['b1', 'b2']] = pd.DataFrame(df['b'].tolist(), index=df.index)

In [7]: df
Out[7]:
   a       b  b1  b2
0  1  (1, 2)   1   2
1  2  (3, 4)   3   4

注意:在早期版本中,此答案建議使用df['b'].apply(pd.Series)而不是pd.DataFrame(df['b'].tolist(), index=df.index) 這也有效(因為它制作了每個元組的系列,然后將其視為數據幀的一行),但它比tolist版本更慢/使用更多內存,如此處的其他答案所述(感謝denfromufa )。

可用於pandas.Series dtype == object pandas.Series對象的str訪問器實際上是一個可迭代的。

假設一個pandas.DataFrame df

df = pd.DataFrame(dict(col=[*zip('abcdefghij', range(10, 101, 10))]))

df

        col
0   (a, 10)
1   (b, 20)
2   (c, 30)
3   (d, 40)
4   (e, 50)
5   (f, 60)
6   (g, 70)
7   (h, 80)
8   (i, 90)
9  (j, 100)

我們可以測試它是否是可迭代的:

from collections import Iterable

isinstance(df.col.str, Iterable)

True

然后我們可以像其他可迭代對象一樣從中分配:

var0, var1 = 'xy'
print(var0, var1)

x y

最簡單的解決方案

所以在一行中,我們可以分配兩列:

df['a'], df['b'] = df.col.str

df

        col  a    b
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

更快的解決方案

稍微復雜一點,我們可以使用zip來創建一個類似的迭代:

df['c'], df['d'] = zip(*df.col)

df

        col  a    b  c    d
0   (a, 10)  a   10  a   10
1   (b, 20)  b   20  b   20
2   (c, 30)  c   30  c   30
3   (d, 40)  d   40  d   40
4   (e, 50)  e   50  e   50
5   (f, 60)  f   60  f   60
6   (g, 70)  g   70  g   70
7   (h, 80)  h   80  h   80
8   (i, 90)  i   90  i   90
9  (j, 100)  j  100  j  100

排隊

意思是,不要改變現有的df

這是有效的,因為assign采用關鍵字參數,其中關鍵字是新(或現有)列名,值將是新列的值。 您可以使用字典並使用**解壓縮它,並將其用作關鍵字參數。

因此,這是分配名為'g'的新列的巧妙方法,該列是df.col.str迭代中的第一項,而'h'df.col.str迭代中的第二項:

df.assign(**dict(zip('gh', df.col.str)))

        col  g    h
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

我的list方法版本

具有現代列表理解和變量解包。 注意:也使用join內聯

df.join(pd.DataFrame([*df.col], df.index, [*'ef']))

        col  g    h
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

變異版本將是

df[['e', 'f']] = pd.DataFrame([*df.col], df.index)

天真時間測試

短數據幀

使用上面定義的一個:

df = pd.concat([df] * 1000, ignore_index=True)

%timeit df.assign(**dict(zip('gh', df.col.str)))
%timeit df.assign(**dict(zip('gh', zip(*df.col))))
%timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh']))

11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
長數據幀

大 10^3 倍

df = pd.concat([df] * 1000, ignore_index=True) %timeit df.assign(**dict(zip('gh', df.col.str))) %timeit df.assign(**dict(zip('gh', zip(*df.col)))) %timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh'])) 11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

在更大的數據集上,我發現.apply()pd.DataFrame(df['b'].values.tolist(), index=df.index)慢幾個數量級。

此性能問題已在 GitHub 中關閉,盡管我不同意此決定:

性能問題 - 適用於 pd.Series vs tuple #11615

它基於這個答案

我認為更簡單的方法是:

>>> import pandas as pd
>>> df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]})
>>> df
   a       b
0  1  (1, 2)
1  2  (3, 4)
>>> df['b_a'] = df['b'].str[0]
>>> df['b_b'] = df['b'].str[1]
>>> df
   a       b  b_a  b_b
0  1  (1, 2)    1    2
1  2  (3, 4)    3    4

第二種解決方案的警告,

pd.DataFrame(df['b'].values.tolist())

是它將顯式丟棄索引,並添加默認的順序索引,而接受的答案

apply(pd.Series)

不會,因為 apply 的結果將保留行索引。 雖然順序最初是從原始數組中保留的,但 Pandas 將嘗試匹配來自兩個數據幀的索引。

如果您嘗試將行設置為數字索引數組,這可能非常重要,並且 Pandas 會自動嘗試將新數組的索引與舊數組的索引匹配,並導致排序出現一些失真。

更好的混合解決方案是將原始數據幀的索引設置為新的,即

pd.DataFrame(df['b'].values.tolist(), index=df.index)

這將保留使用第二種方法的速度,同時確保在結果上保留順序和索引。

extra 是另一種選擇,使用來自https://opendataportal-lasvegas.opendata.arcgis.com/datasets/restaurant-inspections-open-data/explore的數據

import pandas as pd
df = pd.read_csv('raw_data.csv', low_memory=False)
df[['latitude', 'longitude']] = df['Location_1'].str.extract(pat = '(-?\d+\.\d+),\s*(-?\d+\.\d+)')
df.to_csv('result.csv')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM