Python 如何在数据帧中拆分元组列?

Python 如何在数据帧中拆分元组列?,python,numpy,pandas,dataframe,tuples,Python,Numpy,Pandas,Dataframe,Tuples,我有一个熊猫数据框(这只是一小块) 我想拆分所有包含元组的列。例如,我想将列LCV替换为列LCV-a和LCV-b 我该怎么做呢?您可以通过对该列执行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(

我有一个熊猫数据框(这只是一小块)

我想拆分所有包含元组的列。例如,我想将列
LCV
替换为列
LCV-a
LCV-b


我该怎么做呢?

您可以通过对该列执行
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
版本慢/使用更多内存,正如这里的其他答案所指出的(谢谢)。

在更大的数据集上,我发现
.apply()
pd.dataframe慢几个数量级(df['b'].values.tolist(),index=df.index)

此性能问题已在GitHub中解决,但我不同意此决定:


它基于第二种解决方案的警告

pd.DataFrame(df['b'].values.tolist())
它将显式地丢弃索引,并添加一个默认的顺序索引,而接受的答案

apply(pd.Series)
不会,因为apply的结果将保留行索引。虽然最初保留原始数组中的顺序,但Pandas将尝试匹配两个数据帧中的索引

如果您试图将行设置为数字索引数组,并且Pandas将自动尝试将新数组的索引与旧数组的索引相匹配,这一点非常重要,并且会导致排序出现一些扭曲

更好的混合解决方案是将原始数据帧的索引设置到新数据帧上,即

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

这将保持使用第二种方法的速度,同时确保在结果上保留顺序和索引。

我认为更简单的方法是:

>>> 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

dtype==object的
pandas.Series
对象可用的
str
访问器实际上是一个iterable

假设一个
df.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)
我们可以测试它是否为iterable:

from collections import Iterable

isinstance(df.col.str, Iterable)

True
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(**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
然后,我们可以像分配其他iterables一样从中分配:

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
创建类似的iterable:

from collections import Iterable

isinstance(df.col.str, Iterable)

True
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(**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

内联 也就是说,不要变异现有的
df

这是因为
assign
采用关键字参数,其中关键字是新(或现有)列名,值将是新列的值。您可以使用字典,将其与
**
一起解包,并将其用作关键字参数

因此,这是一种巧妙的方法,可以指定一个名为
'g'
的新列,该列是
df.col.str
iterable中的第一项,而
'h'
df.col.str
iterable中的第二项:

from collections import Iterable

isinstance(df.col.str, Iterable)

True
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(**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

列表的我的版本
方法 具有现代列表理解和变量解包功能。 注意:也使用
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)

朴素时间检验 短数据帧 使用上面定义的方法:

%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']))

1.16 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
635 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
795 µs ± 42.5 µs per loop (mean ± std. dev. of 7 runs, 1000 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)

有没有一种方法可以自动实现它,因为它有大量的列?不是我直接想到的。但是,如果你有大量的列,你可以考虑使用“上面的代码”来写它的函数(+,去掉原来的一个):你可以用“熔化函数”来做这件事。工作正常,但对于大型数据集,它会消耗大量内存,并可能导致内存错误。如果没有
,则会导致pd.DataFrame(df['b'].tolist())
。值也可以正常工作。(谢谢,您的解决方案比
.apply()
快得多。)我担心捕获索引,因此@denfromufa显式使用.values.solution可以超快速地使用df['b1','b2']]=pd.DataFrame(df['b'].values.tolist(),index=df.index),并且不会导致内存错误(与.apply(pd.Series))相比)我根据您的索引观察编辑了我的答案,谢谢!这个解决方案确实多得多simpler@jinhuawang这似乎是在
str
表示
pd.Series
对象的基础上进行的黑客攻击。你能解释一下这是如何工作的吗?!我想这就是str对象的工作方式吗?你可以使用strWhat访问数组对象,如果行的一部分太长了,读不下去了?我想这应该是一个接受的数字。它是“熊猫音调”……如果是这样的话。考虑添加TL;Dr:<代码> DF[A' ],DF['B']=DF.CO.STR :