熬夜整理,Pandas最常见的10个问题解答!
StactOverFlow论坛十问
问题1:
如何遍历Pandas中Dataframe对象的行
https://stackoverflow.com/questions/16476924/how-to-iterate-over-rows-in-a-dataframe-in-pandas
如下是一个Pandas中的DataFrame对象
import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df
运行代码输出:
c1 c2
0 10 100
1 11 110
2 12 120
我想通过遍历这个结果集的行,以及列名读取每个单元格中的值,例如:
for row in df.rows:
print row['c1'], row['c2']
Pandas中有这样的方法吗?
我在论坛里发现了另外一个类似的问题,但是没有我想要的答案。例如,他建议使用如下代码:
for date, row in df.T.iteritems():
或者
for row in df.iterrows():
但是我不太理解这个row对象,我该如何使用它?
最佳答案:
DataFrame.iterrows 是一个能够同时生成行索引和行对象(以Series形式输出)的生成器,例如:
import pandas as pd
df = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})
for index, row in df.iterrows():
print(row['c1'], row['c2'])
输出:
10 100
11 110
12 120
问题2:
如何通过列值来选择一个DataFrame对象的行?
https://stackoverflow.com/questions/17071871/how-do-i-select-rows-from-a-dataframe-based-on-column-values
我如何能够通过Pandas中Dataframe对象的某列值来选择对应的行呢?
比如在SQL中,我们会这样用:
SELECT *
FROM table
WHERE column_name = some_value
看了下Pandas官方文档,似乎没有答案。
最佳答案
选择属于某个特定列值对应的行,如some_value,可以使用==:
df.loc[df['column_name'] == some_value]
选择属于某个列值序列对应的行,如some_values,可以使用isin:
df.loc[df['column_name'].isin(some_values)]
结合多个条件的筛选可以使用 &:
df.loc[(df['column_name'] >= A) & (df['column_name'] <= B)]
注意这里的括号。由于Python中的运算符优先级规则,& 符作用域要比 <= 和 >=要窄, 因此,刚才代码中的括号是必须要添加上的。
如果没有括号就是如下情况:
df['column_name'] >= A & df['column_name'] <= B
就等同于:
df['column_name'] >= (A & df['column_name']) <= B
这将产生Series对象的真值是模糊的错误。(Truth value of a Series is ambiguous error.)
如果要选择对应列值不等于某个值对应的行,可以使用!=:
df.loc[df['column_name'] != some_value]
isin会返回一个布尔型Series对象,因此要选择不等于某些列值的行时,可以将对应的布尔型序列进行取反:
df.loc[~df['column_name'].isin(some_values)]
例如:
import pandas as pd
import numpy as np
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
'B': 'one one two three two two one three'.split(),
'C': np.arange(8), 'D': np.arange(8) * 2})
print(df)
# A B C D
# 0 foo one 0 0
# 1 bar one 1 2
# 2 foo two 2 4
# 3 bar three 3 6
# 4 foo two 4 8
# 5 bar two 5 10
# 6 foo one 6 12
# 7 foo three 7 14
print(df.loc[df['A'] == 'foo'])
输出:
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
如果你有多个值想要包含进去,那可以把他们放入一个列表中(或者任意的序列类结构中)并且使用isin
print(df.loc[df['B'].isin(['one','three'])])
输出:
A B C D
0 foo one 0 0
1 bar one 1 2
3 bar three 3 6
6 foo one 6 12
7 foo three 7 14
不过注意,更有效地方式是将你要查找的列指定为列索引然后再去使用df.loc方法去定位指定的列值,会更有效率:
df = df.set_index(['B'])
print(df.loc['one'])
yields
A C D
B
one foo 0 0
one bar 1 2
one foo 6 12
或者,通过df.index.isin筛选指定行索引中包含多个你的指定值,来筛选结果集: or, to include multiple values from the index use df.index.isin:
df.loc[df.index.isin(['one','two'])]
输出:
A C D
B
one foo 0 0
one bar 1 2
two foo 2 4
two foo 4 8
two bar 5 10
one foo 6 12
问题3
重命名Pandas中的列名
https://stackoverflow.com/questions/11346283/renaming-column-names-in-pandas
我有一个DataFrame对象需要对它原始的列标签进行替换,我想对这个DataFrame对象A做如下操作,它起始的列名如下:
['$a', '$b', '$c', '$d', '$e']
需要转化为:
['a', 'b', 'c', 'd', 'e']
我已经将编辑好的列名储存到了一个列表中,但是不知道如何能够替换DataFrame的列名
最佳答案
只需要对列属性.columns进行指定即可
>>> df = pd.DataFrame({'$a':[1,2], '$b': [10,20]})
>>> df
$a $b
0 1 10
1 2 20
>>> df.columns = ['a', 'b']
>>> df
a b
0 1 10
1 2 20
问题4:
如何删除DataFrame对象中的列
https://stackoverflow.com/questions/13411544/delete-a-column-from-a-pandas-dataframe
当我们删除DataFrame中的一列时我会使用:
del df['column_name']
这个没有问题,但是为什么这种用法就不行呢?
del df.column_name
既然能够通过df.column_name访问列或者Series对象,我觉得应该可以得到同样的效果。
最佳答案:
正如你提到的,正确的写法是:
del df['column_name']
由于Python的同步限制,很难通过del df.column_name来达到删除的作用,因为在Python的底层 del df[name] 等价于df.delitem(name)
问题5:
如何选择DataFrame中的多列:
https://stackoverflow.com/questions/11285613/selecting-multiple-columns-in-a-pandas-dataframe
我有一个不同列组成的数据,但是我不知道如何提取某些列数据并将他们保存到另外一个变量中去,
比如:
index a b c
1 2 3 4
2 3 4 5
我该如何把选中的‘a’,‘b’列保存另外一个变量df1中呢?
我的方法是:
df1 = df['a':'b']
df1 = df.ix[:, 'a':'b']
但上面的方法都不能解决问题。
最佳答案
列名由于是字符串,它不能通过你使用的切片方式进行提取。
你其实有多种选择,如果你知道哪些变量是想要提取出来的,你可以传入一个包含这些列的列表到底层的_getitem_方法中,也就是[]花式索引,例如:
df1 = df[['a', 'b']]
此外,如果是通过数字来索引,而不是通过它们的名字(例如你的代码可以忽略列名而是直接提取前两列)那你可以使用下面的方法:
df1 = df.iloc[:, 0:2] # 注意Python的切片是不能够提取结尾的索引编号对应的值的
此外,你还要熟悉一个思维方式,访问Pandas对象和复制这个对象的区别,刚才我提供的第一种方法是在内容中返回了一个想要的对象的复制品。
当然有时,Pandas中的索引规范并非全都遵循这样的规则,而是会给你一个全新的变量,他会引用相同的内存空间作为一个次级的对象,或者源对象的一个片段。这种方式一般是我给你展示的第二种方法中的原理,因此你可以通过.copy()修改它,获取到一个正常的复制品。不过这种情况下,改变你认为的切片对象有时会改变原有的对象,因此要格外小心这种情况的发生。
df1 = df.iloc[0, 0:2].copy() # 为了避免同时改变df1和df2,你可以这样操作
使用iloc方法时,你需要找到列的位置,你可以通过使用get_loc方法准确获取列的索引,
{df.columns.get_loc(c): c for idx, c in enumerate(df.columns)}
现在使用这个字典来获取列名和iloc方法是不是更方便了呢?
问题6:
https://stackoverflow.com/questions/15943769/how-do-i-get-the-row-count-of-a-pandas-dataframe
如何数Pandas中DataFrame对象的行数?
我尝试通过以下两种Pandas中DataFrame的方法来获取行数,但是均告失败,请问我错在哪里,请看代码.
方法1:
total_rows = df.count
print total_rows + 1
方法2:
total_rows = df['First_column_label'].count
print total_rows + 1
报错信息均是:
TypeError: unsupported operand type(s) for +: 'instancemethod' and 'int'
最佳回答
对于DataFrame对象,你可以用下面的任何一种方法来实现对行数的计数:
len(df.index)
df.shape[0]
df[df.columns[0]].count() # 等价于统计第一列中的非空元素的行数
也可以通过图像编码来展示:
import numpy as np
import pandas as pd
import perfplot
perfplot.save(
"out.png",
setup=lambda n: pd.DataFrame(np.arange(n * 3).reshape(n, 3)),
n_range=[2**k for k in range(25)],
kernels=[
lambda df: len(df.index),
lambda df: df.shape[0],
lambda df: df[df.columns[0]].count(),
],
labels=["len(df.index)", "df.shape[0]", "df[df.columns[0]].count()"],
xlabel="Number of rows",
)
问题7:
https://stackoverflow.com/questions/13148429/how-to-change-the-order-of-dataframe-columns
如何改变DataFrame对象中列的顺序?
不罗嗦上代码,假设我有如下DataFrame对象:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.rand(10, 5))
我增加了一列通过以下方式:
df['mean'] = df.mean(1)
我如何才能把mean这一列放到前面,比如把mean列放到第一列,保持其他列不动?
最佳答案
一个比较简单的方法就是重新给DataFrame对象传入一个你需要次序的列的列表结构,请看代码:
In [6]: df
Out[6]:
0 1 2 3 4 mean
0 0.445598 0.173835 0.343415 0.682252 0.582616 0.445543
1 0.881592 0.696942 0.702232 0.696724 0.373551 0.670208
2 0.662527 0.955193 0.131016 0.609548 0.804694 0.632596
3 0.260919 0.783467 0.593433 0.033426 0.512019 0.436653
4 0.131842 0.799367 0.182828 0.683330 0.019485 0.363371
5 0.498784 0.873495 0.383811 0.699289 0.480447 0.587165
6 0.388771 0.395757 0.745237 0.628406 0.784473 0.588529
7 0.147986 0.459451 0.310961 0.706435 0.100914 0.345149
8 0.394947 0.863494 0.585030 0.565944 0.356561 0.553195
9 0.689260 0.865243 0.136481 0.386582 0.730399 0.561593
In [7]: cols = df.columns.tolist()
In [8]: cols
Out[8]: [0L, 1L, 2L, 3L, 4L, 'mean']
你可以随意调整cols的顺序,下面是我如何把最后一个元素调整到第一个位置的:
In [12]: cols = cols[-1:] + cols[:-1]
In [13]: cols
Out[13]: ['mean', 0L, 1L, 2L, 3L, 4L]
然后重新调整DataFrame对象如下:
In [16]: df = df[cols] # OR df = df.ix[:, cols]
In [17]: df
Out[17]:
mean 0 1 2 3 4
0 0.445543 0.445598 0.173835 0.343415 0.682252 0.582616
1 0.670208 0.881592 0.696942 0.702232 0.696724 0.373551
2 0.632596 0.662527 0.955193 0.131016 0.609548 0.804694
3 0.436653 0.260919 0.783467 0.593433 0.033426 0.512019
4 0.363371 0.131842 0.799367 0.182828 0.683330 0.019485
5 0.587165 0.498784 0.873495 0.383811 0.699289 0.480447
6 0.588529 0.388771 0.395757 0.745237 0.628406 0.784473
7 0.345149 0.147986 0.459451 0.310961 0.706435 0.100914
8 0.553195 0.394947 0.863494 0.585030 0.565944 0.356561
9 0.561593 0.689260 0.865243 0.136481 0.386582 0.730399
问题8:
https://stackoverflow.com/questions/19482970/get-a-list-from-pandas-dataframe-column-headers
获取Pandas中DataFrame列标签的列表
我想要得到一个由Pandas中DataFrame对象的列标签组成的列表,这个DataFrame对象来自用户的输入,因此我不知道有多少列或者他们名字是什么。
举个例子:
我有如下DataFrame对象
>>> my_dataframe
y gdp cap
0 1 2 5
1 2 3 9
2 8 7 2
3 3 4 7
4 6 7 7
5 4 8 3
6 8 2 8
7 9 9 10
8 6 6 4
9 10 10 7
这是我想要的结果
>>> header_list
['y', 'gdp', 'cap']
最佳回答
你可以通过如下方式得到列标签列表:
list(my_dataframe.columns.values)
你也可以简单的使用如下方法(Ed Chum的答案之前提过)
list(my_dataframe)
问题9:
https://stackoverflow.com/questions/12555323/how-to-add-a-new-column-to-an-existing-dataframe
如何在已存在的DataFrame对象中添加一个新列
我有如下索引的DataFrame对象,列名均为自定义,且行中是不连续的数字:
a b c d
2 0.671399 0.101208 -0.181532 0.241273
3 0.446172 -0.243316 0.051767 1.577318
5 0.614758 0.075793 -0.451460 -0.012493
我现在想在这个DataFrame对象中添加一个新列,‘e’,并且不改变任何其他的结构(例如,新列总是有和这个DataFrame对象相同的长度)
e列会是如下的样子:
0 -0.335485
1 -1.166658
2 -0.385571
dtype: float64
该怎么做?
最佳回答
2017年编辑
正如@Alexdaer在评论中提到的,目前将Series对象值添加到一个DataFrame中的新列里面最佳的方式是通过赋值,代码如下:
df1 = df1.assign(e=pd.Series(np.random.randn(sLength)).values)
2015年编辑
有些情况下,使用如下代码会产生SettingWithCopyWarning的错误信息,但是,对于Pandas为0.16.1的版本还是可以顺畅的运行这个代码的:
>>> sLength = len(df1['a'])
>>> df1
a b c d
6 -0.269221 -0.026476 0.997517 1.294385
8 0.917438 0.847941 0.034235 -0.448948
>>> df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)
>>> df1
a b c d e
6 -0.269221 -0.026476 0.997517 1.294385 1.757167
8 0.917438 0.847941 0.034235 -0.448948 2.228131
>>> pd.version.short_version
'0.16.1'
SettingWithCopyWarning警告在于提示对于DataFrame对象复制品的无效赋值。当然,并不是说你错了,但是到了0.1.30版本它会提醒你更多合适的方法来满足需求。然后,如果你得到警告,尝试使用如下.loc[row_index,col_indexer] = value
>>> df1.loc[:,'f'] = pd.Series(np.random.randn(sLength), index=df1.index)
>>> df1
a b c d e f
6 -0.269221 -0.026476 0.997517 1.294385 1.757167 -0.050927
8 0.917438 0.847941 0.034235 -0.448948 2.228131 0.006109
>>>
其实,在Pandas的官方文档里有更加高效的方法:
原答案:
使用使用源对象df1的行索引indexes来创建Series对象:
df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)
问题10
https://stackoverflow.com/questions/15891038/change-column-type-in-pandas
改变DataFrame对象列的数据类型
我想把一个二维数组转化成为一个Pandas的DataFrame对象,如下所示:
a = [['a', '1.2', '4.2'], ['b', '70', '0.03'], ['x', '5', '0']]
df = pd.DataFrame(a)
怎样做才能让每一列对应它们的应有的数据类型呢?以刚才的例子来说,第二列和第三列,有没有什么方法能够在转化为DataFrame对象时候指定它们为浮点数类型?是不是先建立一个DataFrame对象然后遍历所有的列,然后再改变他们的数据类型?我其实更倾向于动态处理,因为有上百个列的时候,全部都要一一指定列的数据类型太恐怖了。不过我能保证的是每一类的数据类型都是一致的。
最佳答案
Pandas中你主要有4中方法来转化类型:
to_numeric() - 提供了安全的非数字类型到数字类型的转化方式(同样可以参考下to_datetime() 和 to_timedelta()方法)
astype() - 几乎可以转化任何类型数据为其他任意数据类型(即使没什么必要这么做)。你还可以使用它去转化分类对象(很有用哦)
infer_objects() - 一个可以将存储Python对象的对象列转化为pandas类型的实用方法
convert_dtypes() - 将DataFrame对象的列转化为最可能的dtype类型数据,支持pd.NA(pandas中缺失值的表示方式)
接下来我详细阐释下他们的作用:
1. to_numeric()
这个方法是转化一个或者多个DataFrame列数据类型为数值类型的最佳方法。
这个函数会尽可能尝试将非数值对象(例如strings字符串)转化为整数或者浮点型。
基本用法: to_numeric()传入的参数一般是一个Series对象或者一个DataFrame的列
>>> s = pd.Series(["8", 6, "7.5", 3, "0.9"]) # 混合数值和字符串类型
>>> s
0 8
1 6
2 7.5
3 3
4 0.9
dtype: object
>>> pd.to_numeric(s) # 将所有数据转化为了浮点型
0 8.0
1 6.0
2 7.5
3 3.0
4 0.9
dtype: float64
As you can see, a new Series is returned. Remember to assign this output to a variable or column name to continue using it: 如上述代码,最终返回了一个新的Seris对象,如果要继续使用这个对象,你需要添加到一个新的变量或者列名变量中
转化Series对象
my_series = pd.to_numeric(my_series)
转化一个DataFrame对象的a列
df["a"] = pd.to_numeric(df["a"])
你也可以使用apply方法将它作用于多个列:
转化所有列的数据类型
df = df.apply(pd.to_numeric) # 转化DataFrame对象中所有的列的数据类型
仅仅转化列‘a’和‘b’
df[["a", "b"]] = df[["a", "b"]].apply(pd.to_numeric)
只要你的值可以做转换,以上方法足够了。
错误处理
如果有些值无法转为数值型怎么办?
to_numeric()方法还提供了errors参数用于让你将非数值型数据强制转化为NaN值或者简单的忽略包含这些数据的错误提示。
例如:
>>> s = pd.Series(['1', '2', '4.7', 'pandas', '10'])
>>> s
0 1
1 2
2 4.7
3 pandas
4 10
dtype: object
默认情况下如果遇到不可以转化的数据,就会报错,上面的例子中它不会处理字符串‘pandas’
>>> pd.to_numeric(s) # or pd.to_numeric(s, errors='raise')
ValueError: Unable to parse string
相比于失败,我们可能更希望‘pandas’这个字符串被转化为缺失值或者异常值。因此我们将这样的值转化NaN值,通常我们会通过参数argument来指定:
>>> pd.to_numeric(s, errors='coerce')
0 1.0
1 2.0
2 4.7
3 NaN
4 10.0
dtype: float64
第三种处理转化错误的方法就是忽略掉它们:
>>> pd.to_numeric(s, errors='ignore')
# the original Series is returned untouched
最后一个转化方法在我们要转化整个DataFrame对象时特别有用,尤其时我们不知道其中的列会被转化成那种数值类型,这种情况下,可以使用如下操作:
df.apply(pd.to_numeric, errors='ignore')
上述函数的使用会使我们的DataFrame对象中的数值型数据转化为数值型,而非数值型就保持原有的状态(例如:纯字符串类型) 向下转型:
默认情况下,使用to_numeric()函数转化数据会得到int64位或者float64位数据(或者取决于你的平台类型产生数据长度和类型)
这通常是满足我们需求的,但是假设现在你需要占用更少的内存空间比如float32 或者 int8,该怎么办呢?
to_numeric()提供了一系列的选项来进行向下转型,包括 'integer', 'signed', 'unsigned', 'float'. 下面是一个整型Series对象:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
向下转型时,指定downcast参数为‘interger’,便可得到内存占用尽可能小的空间来容纳这个对象。
>>> pd.to_numeric(s, downcast='integer')
0 1
1 2
2 -7
dtype: int8
设置downcast的浮点型参数,便可以获得一个比正常浮点数小的浮点类型
>>> pd.to_numeric(s, downcast='float')
0 1.0
1 2.0
2 -7.0
dtype: float32
2. astype()
astype函数能够使你明确指定你的DataFrame或者Series对象的数据类型为你想要指定的。它是非常灵活的一个函数,允许你很容易的进行数据间转化。
基本用法:
只需要调用该方法指定为你想要的数据类型,比如Numpy类型(np.int16),一些Python类型(如:bool),或者特殊的pandas类型(比如categorical类型)。该方法都能够尽可能的尝试将他们进行转化
接下来是一些使用示例:
转化所有的DataFrame列为int64类型
df = df.astype(int)
将‘a’列转化为int64的数据类型以及‘b’列转化为混合(complex)数据类型
将Series对象转化为float16类型
s = s.astype(np.float16)
将Series对象转化为Python字符串
s = s.astype(str)
将series对象转化为分类(categorical)类型 - 更多细节可以参考官方文档
s = s.astype('category')
你还记得我曾说过astype()会尝试去将Series或者DataFrame对象的值进行转化吗?如果它不能判断如何转化,系统就会报错,例如你有NaN值或者inf值,当你将它们转化为一个整数就会报错。
当Pandas版本是0.20.0时,这个错误可以通过传入errors=‘ignore’来忽略掉,你原有的数据不会发生任何改变。
注意: astype()确实很强大,但是有时候转化数值时也会发生错误。例如:
>>> s = pd.Series([1, 2, -7])
>>> s
0 1
1 2
2 -7
dtype: int64
接下来时一些位数比较小的整数,那么把他们转化为没有符号的8 个字节的类型来省些内存空间,会怎么样?
>>> s.astype(np.uint8)
0 1
1 2
2 249
dtype: uint8
这个转化确实可行,但是你会发现-7被转化成了249
使用pd.to_numeric(s,downcast='unsigned')函数可以规避上面的错误
3. infer_objects()
Pandas的0.21.0版本后引入了方法infer_objects()用来将DataFrame对象的列转化为一个对象的数据类型为一个更为准确的类型(软转化)
例如:这里是一个DataFrame对象包含两列对象类型。其一存放了真实的数字类型,另外一个是字符型的数字类型,
>>> df = pd.DataFrame({'a': [7, 1, 5], 'b': ['3','2','1']}, dtype='object')
>>> df.dtypes
a object
b object
dtype: object
使用infer_objects()你可以把a列转化为int64类型:
>>> df = df.infer_objects()
>>> df.dtypes
a int64
b object
dtype: object
‘b'列并没有被处理因为它是字符串而非整型。如果你想让两列同时转化,你可以使用astype(int)方法转化。
4. convert_dtypes()
1.0版本或以上包含了convert_dtypes()函数用于将Series和DataFrame对象中的columns转化为最佳的数据类型从而支持pd.NA缺失值类型。
这里的’最佳‘指的是最适宜存储指定数据的数据类型。例如:如果一个pandas的对象中的值全部是整数类型或者缺失值类型:那么Python整数类型的列将被转化为Int64类型,而Numpy int32类型的数值就会编程Pandas类型的Int32
这里我们有个示例DataFrame对象,我们可以得到以下结果:
>>> df.convert_dtypes().dtypes
a Int64
b string
dtype: object
由于’a‘列存放的是整数数值,它可以被转化为Int64类型(这使得它不像int64类型,可以存放缺失值,)
’b‘列包含字符串对象,因此可以被转化为Pandas的字符串类型。
默认情况下,这个方法只要用于每列中的对象型数据。我可以通过设置infer_objects=False取消这个默认选项。
>>> df.convert_dtypes(infer_objects=False).dtypes
a object
b string
dtype: object
现在’a‘列仍然是一个对象列:pandas知道它可以被描述为’整型‘列(内部运行了infer_dtype方法)但是并没有准确的指出它应该成为的哪种整型类型。而’b‘列之所以被转化为字符串数据主要是因为他被识别为了’字符串‘数据。
关注蚂蚁老师,学习更多Pandas干货知识!