我正在尝试将我的Stata代码重新编程为Python以提高速度,我指出了PANDAS的方向。但是,我很难绕过如何处理数据。
假设我想迭代列标题'ID'中的所有值。如果该ID与特定数字匹配,那么我想更改两个对应的值FirstName和LastName。
在Stata看起来像这样:
replace FirstName = "Matt" if ID==103
replace LastName = "Jones" if ID==103
因此,这将FirstName中与ID == 103的值对应的所有值替换为Matt。
在PANDAS,我正在尝试这样的事情
df = read_csv("test.csv")
for i in df['ID']:
if i ==103:
...
不知道从哪里开始。有什么想法吗?
答案 0 :(得分:112)
一种选择是使用Python的切片和索引功能来逻辑评估条件所在的位置并覆盖那里的数据。
假设您可以使用pandas
将数据直接加载到pandas.read_csv
,那么以下代码可能会对您有所帮助。
import pandas
df = pandas.read_csv("test.csv")
df.loc[df.ID == 103, 'FirstName'] = "Matt"
df.loc[df.ID == 103, 'LastName'] = "Jones"
如评论中所述,您也可以一次性对两个列进行分配:
df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'
请注意,您需要使用pandas
0.11或更新版本才能使用loc
进行覆盖分配操作。
另一种方法是使用所谓的链式赋值。这种行为不太稳定,所以它不被认为是最好的解决方案(在文档中它是explicitly discouraged),但了解它是有用的:
import pandas
df = pandas.read_csv("test.csv")
df['FirstName'][df.ID == 103] = "Matt"
df['LastName'][df.ID == 103] = "Jones"
答案 1 :(得分:25)
您可以使用map
,它可以映射来自dictonairy甚至自定义函数的值。
假设这是你的df:
ID First_Name Last_Name
0 103 a b
1 104 c d
创建dicts:
fnames = {103: "Matt", 104: "Mr"}
lnames = {103: "Jones", 104: "X"}
并映射:
df['First_Name'] = df['ID'].map(fnames)
df['Last_Name'] = df['ID'].map(lnames)
结果将是:
ID First_Name Last_Name
0 103 Matt Jones
1 104 Mr X
或使用自定义功能:
names = {103: ("Matt", "Jones"), 104: ("Mr", "X")}
df['First_Name'] = df['ID'].map(lambda x: names[x][0])
答案 2 :(得分:8)
原始问题涉及特定的狭义用例。对于那些需要更多通用答案的人来说,这里有一些例子:
鉴于以下数据框:
import pandas as pd
import numpy as np
df = pd.DataFrame([['dog', 'hound', 5],
['cat', 'ragdoll', 1]],
columns=['animal', 'type', 'age'])
In[1]:
Out[1]:
animal type age
----------------------
0 dog hound 5
1 cat ragdoll 1
下面我们使用description
操作添加一个新的+
列作为其他列的串联,该操作被重写为系列。花哨的字符串格式,f字符串等在这里不起作用,因为+
适用于标量,而不是原始的#39;值:
df['description'] = 'A ' + df.age.astype(str) + ' years old ' \
+ df.type + ' ' + df.animal
In [2]: df
Out[2]:
animal type age description
-------------------------------------------------
0 dog hound 5 A 5 years old hound dog
1 cat ragdoll 1 A 1 years old ragdoll cat
对于我们将使用条件修正的cat(而不是1 years
),我们得到1 year
。
我们将使用其他列中的值替换原始animal
列,并使用np.where
根据age
的值设置条件子字符串:
# append 's' to 'age' if it's greater than 1
df.animal = df.animal + ", " + df.type + ", " + \
df.age.astype(str) + " year" + np.where(df.age > 1, 's', '')
In [3]: df
Out[3]:
animal type age
-------------------------------------
0 dog, hound, 5 years hound 5
1 cat, ragdoll, 1 year ragdoll 1
更灵活的方法是在整个数据框而不是单个列上调用.apply()
:
def transform_row(r):
r.animal = 'wild ' + r.type
r.type = r.animal + ' creature'
r.age = "{} year{}".format(r.age, r.age > 1 and 's' or '')
return r
df.apply(transform_row, axis=1)
In[4]:
Out[4]:
animal type age
----------------------------------------
0 wild hound dog creature 5 years
1 wild ragdoll cat creature 1 year
在上面的代码中,transform_row(r)
函数使用表示给定行的Series
对象(由axis=1
表示),默认值axis=0
将提供{{1}每列的对象)。这简化了处理,因为我们可以访问实际的原语'使用列名称在行中的值,并具有给定行/列中其他单元格的可见性。
答案 3 :(得分:7)
这个问题可能仍然经常被访问,因此值得为卡西斯先生的回答提供附录。可以对dict
内置类进行子类化,以便为“缺失”键返回默认值。这种机制适用于大熊猫。 但请参见下文。
通过这种方式可以避免关键错误。
>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> class SurnameMap(dict):
... def __missing__(self, key):
... return ''
...
>>> surnamemap = SurnameMap()
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap[x])
>>> df
ID Surname
0 101 Mohanty
1 201
2 301 Drake
3 401
同样的事情可以通过以下方式更简单地完成。对dict对象的get
方法使用'default'参数使得不必对dict进行子类化。
>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> surnamemap = {}
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap.get(x, ''))
>>> df
ID Surname
0 101 Mohanty
1 201
2 301 Drake
3 401
答案 4 :(得分:4)
df['FirstName']=df['ID'].apply(lambda x: 'Matt' if x==103 else '')
df['LastName']=df['ID'].apply(lambda x: 'Jones' if x==103 else '')
答案 5 :(得分:0)
我发现打印出每一行满足条件的地方更容易首次亮相:
for n in df.columns:
if(np.where(df[n] == 103)):
print(n)
print(df[df[n] == 103].index)