Python数据清理实用指南
以下所有语句均在pycharm中显示,可以直接复制到pycharm中查看执行结果。
# 数据清理实践
#1.1 数据标准化:清理、转换、合并、重塑
#1.2 数据聚合和分组操作
将numpy 导入为np
将pandas 导入为PD
#2 数据标准化:清理、转换、合并、重塑
#合并数据集
#2.1 pandas.nmerge: 可以根据一个或多个键链接不同数据帧的行
#2.2 pandas.concat: 沿轴堆叠多个对象
#2.3 merge_first: 可以合并重复数据并用另一个对象的值填充一个对象中的缺失值
# 3. 合并数据库风格的DataFrame
#3.1 数据集合并或连接操作:通过一个或多个键链接行
df1=pd.DataFrame({\'key\':[\'b\',\'b\',\'a\',\'c\',\'a\',\'a\',\'b\'],\'data1\':range(7)})
打印(df1)
df2=pd.DataFrame({\'key\':[\'a\',\'b\',\'d\'],\'data2\':range(3)})
打印(df2)
#3.2 多对一合并
打印(pd.合并(df1,df2))
#如果没有指定用于连接的列,则合并将使用指定为键的重复列名,如下所示:
打印(pd.merge(df1,df2,on=\'key\'))
#如果两个对象有不同的列名,可以分别指定。
df3=pd.DataFrame({\'1key\':[\'b\',\'b\',\'a\',\'c\',\'a\',\'a\',\'b\'],\'data1\':range(7)})
df4=pd.DataFrame({\'rkey\':[\'a\',\'b\',\'d\'],\'data2\':range(3)})
a=pd.merge(df3,df4,left_on=\'1key\',right_on=\'rkey\')
打印(一)
#默认情况下,合并执行内部链接,结果键是交集。外连接获取键的连接
b=pd.merge(df1,df2,how=\'外部\')
打印(二)
#多对多合并操作
c=pd.merge(df1,df2,on=\'key\',how=\'left\')
打印(三)
d=pd.merge(df1,df2,on=\'key\',how=\'right\')
打印(d)
#link方法仅影响结果中出现的键,基于多个键进行合并,并传递列名列表。
left=pd.DataFrame({\'key1\':[\'foo\',\'foo\',\'bar\'],
\'key2\':[\'1\',\'2\',\'1\'],
\'lva1\':[1,2,3]})
打印(左)
right=pd.DataFrame({\'key1\':[\'foo\',\'foo\',\'bar\',\'bar\'],
\'key2\':[\'1\',\'1\',\'1\',\'2\'],
\'rva1\':[4,5,6,7]})
打印(右)
a1=pd.merge(左,右,on=[\'key1\',\'key2\'],how=\'outer\')
打印(a1)
#对第——列执行列转换会破坏Dataframe对象中的索引
#suffixes 选项:指定要附加到左右Dataframe 对象中重复列名的字符串
打印(pd.merge(左,右,on=\'key1\'))
print(pd.merge(左,右,on=\'key1\',后缀=(\'_left\',\'_right\')))
# 按索引合并
#如果数据帧中的连接键位于该索引处,则传递left_index=True, right_index=TrUE 以指示该索引应用作连接键
left1=pd.DataFrame({\'key\':[\'a\',\'b\',\'a\',\'a\',\'b\',\'c\'],
\'vakye\':range(6)})
打印(左1)
right1=pd.DataFrame({\'group_val\':[3.5,7]},index=[\'a\',\'b\'])
打印(右1)
f=pd.merge(left1,right1,left_on=\'key\',right_index=True)
打印(f)
#对于分层索引数据:
lefth=pd.DataFrame({\'key1\':[\'俄亥俄\',\'俄亥俄\',\'俄亥俄\',\'内夫达\',\'内夫达\'],
\'key2\':[2000,2001,2002,2001,2002],
\'数据\':np.arange(5.)})
打印(左)
righth=pd.DataFrame(np.arange(12).reshape((6,2)),
索引=[[\'内华达州\',\'内华达州\',\'俄亥俄州\',\'俄亥俄州\',\'俄亥俄州\',\'俄亥俄州\'],
[2001,2000,2000,2000,2001,2002]],
列=[\'事件1\',\'事件2\'])
打印(右)
g=pd.merge(左,右,left_on=[\'key1\',\'key2\'],right_index=True)
打印量(克)
left2=pd.DataFrame({\'俄亥俄\':[1.3.5.],\'内华达\':[2.4.6.]},
索引=列表(\'ace\'))
打印(左2)
right2=pd.DataFrame({\'密苏里州\':[7.9.11.13.],\'阿拉巴马州\':[8.10.12.14.]}
,索引=列表(\'bcde\'))
打印(右2)
#使用双方索引进行合并。
print(pd.merge(left2,right2,how=\'outer\',left_index=True,right_index=True))
#Dataframe绑定实例方法:
print(left2.join(right2,how=\'outer\'))
#通过索引实现合并,对连接键进行左连接,支持参数dataframe的特定列之间的连接,无论是否有重复列,都是比较有用的。
print(left1.join(right1,on=\'key\'))
#对于简单的索引串联,可以传递一组dataframe进行串联(concat函数也有这个功能)
another=pd.DataFrame([[7.8.],[9.10.],[11.12.],[16.17.]],
索引=列表(\'acef\'),列=[\'纽约\',\'俄勒冈\'])
打印(单独)
print(left2.join([right2,另一个]))
# 轴连接
#数据合并操作
#连接、绑定、堆叠
#Numpy具有串联功能,供用户合并原始Numpy数组
arr=np.arange(12).reshape((3,4))
打印(arr)
print(np.concatenate([arr,arr],axis=1))
#pandas 连接函数:
s1=pd.Series([0,1],索引=[\'a\',\'b\'])
s2=pd.Series([2,3,4],index=list(\'cde\'))
s3=pd.Series([5,6],index=list(\'fg\'))
打印(s1)
打印(s2)
打印(s3)
打印(pd.concat([s1,s2,s3]))
#默认情况下,coacat 使用axis=0。通过传递axis=1 生成DataFrame
打印(pd.concat([s1,s2,s3],轴=1))
#在这种情况下,其他轴上没有重叠。通过join=\'inner\' 来获取它们的交集。
print(pd.concat([s1,s3],axis=1,join=\'inner\'))
# 使用key 参数在连接轴上创建分层索引。
结果=pd.concat([s1,s1,s3],keys=[\'一\',\'二\',\'三\'])
打印(结果)
打印(结果.unstack())
沿着#axis=1 合并系列将完成作为数据帧中的列标题的键。
print(pd.concat([s1,s2,s3],axis=1,keys=[\'一\',\'二\',\'三\']))
这同样适用于#Dataframe 对象。
df1=pd.DataFrame(np.arange(6.).reshape((3,2)),index=list(\'abc\'),
列=[\'1\',\'2\'])
打印(df1)
df2=pd.DataFrame(5+np.arange(4).reshape((2,2)),index=list(\'ac\'),columns=[\'三\',\'四\'])
打印(df2)
print(pd.concat([df1,df2],axis=1,keys=[\'level1\',\'level2\']))
#如果你传递一个字典,字典中的键将被用作keys选项的值。
print(pd.concat({\'level1\':df1,\'level2\':df2},axis=1))
#Dataframe行索引与当前分析无关:
df1=pd.DataFrame(np.random.randn(3,4),列=列表(\'abcd\'))
df2=pd.DataFrame(np.random.randn(2,3),列=列表(\'bda\'))
打印(df1)
打印(df2)
通过#ignore_index=True
打印(pd.concat([df1,df2],ignore_index=True))
打印(pd.concat([df1,df2]))
#合并重复数据
#对于两个索引完全或部分重叠的数据集
#Numpy的where函数用于表达向量化的if -else
a=pd.Series([np.nan,2.5,np.nan,3.5,4.5,np.nan],
索引=列表(\'fedcba\'))
b=pd.Series(np.arange(len(a),dtype=np.float64),
索引=列表(\'fedcba\'))
打印(一)
打印(二)
打印(np.where(pd.isnull(a),b,a))
#Series的combin_first方法实现了与上面相同的功能,并进行数据对齐。
打印(b[:-2].combine_first(a[2:]))
#与数据框相同
df1=pd.DataFrame({\'a\':[1.np.nan,5.np.nan],
\'b\':[np.nan,2.np.nan,6.],
\'c\':range(2,18,4)})
打印(df1)
df2=pd.DataFrame({\'a\':[5.4,np.nan,3.7.],
\'b\':[np.nan,3.4.6.8.]})
打印(df2)
打印(df1.combine_first(df2))
您可以看到#parameter 对象中的数据是调用对象中缺失数据的“补丁”。
#reshape 和轴旋转
#重新排列表格数据的基本操作:重塑和旋转
#重塑层次索引
#stark: 将数据列旋转为行
#unstack:将数据行旋转为列
data=pd.DataFrame(np.arange(6).reshape(2,3),index=pd.Index([\'俄亥俄\',\'科罗拉多\'],name=\'州\',
columns=pd.Index([\'一\',\'二\',\'三\'],name=\'数字\')))
打印数据)
使用#stack 方法将行转换为列以获得系列。
结果=data.stack()
打印(结果)
打印(\' - - - - - - -\')
#对于分层索引系列,您可以使用unstack 将其重新排列成数据帧。
打印(结果.unstack())
#默认情况下,unstack操作的是最内层
#要取消堆叠其他级别,请传递层次结构级别的编号或名称。
打印(结果.unstack(0))
打印(结果.unstack(\'状态\'))
#如果在每组:中都没有找到所有级别值,则Unstack操作可能会导致值数据丢失
s1=pd.Series([0,1,2,3],index=list(\'abcd\'))
s2=pd.Series([4,5,6],index=list(\'cde\'))
data2=pd.concat([s1,s2],keys=[\'一\',\'二\'])
打印(data2.unstack())
#stack默认排除缺失数据,因此操作是可逆的。
打印(data2.unstack()。stack())
打印(data2.unstack().stack(dropna=False))
#当对数据框执行unstack操作时,用作旋转轴的级别将是结果中的最低级别。
df=pd.DataFrame({\'左\':结果,\'右\':结果+5},
columns=pd.Index([\'左\',\'右\'],name=\'侧\'))
打印(df)
打印(df.unstack(\'状态\'))
print(df.unstack(\'state\').stack(\'side\'))
# 将“长格式”旋转为“节格式”
#时间序列数据通常以“长”或“堆叠”格式存储在数据库和CSV 中。
ldata=pd.DataFrame({\'data\':[\'1959-3-31\',\'1959-3-31\',\'1959-3-31\',\'1959-6-30\',\'1959-6-30 \',\'1959-6-30\'],
\'item\':list(\'六六\'),
\'值\':[2710,0,5,2778,2,5]})
打印(ldata)
将其转换为#DataFrame 并使用pivot 方法。
枢轴=pd.pivot_table(ldata,索引=[\'数据\',\'项目\'])
打印(旋转.head())
#检索到的Datafrme具有分层列。
枢轴=ldata.pivot(\'数据\',\'项目\')
打印(旋转)
枢轴[\'值\']
#假设我们有两个数据列需要重新整形。
ldata[\'value2\']=np.random.randn(len(ldata))
打印(ldata)
#pivot 实际上只是一个快捷方式。使用set_index 创建分层索引并使用unstack 重塑它。
unstacked=ldata.set_index([\'data\',\'item\']).unstack(\'item\')
打印(取消堆叠)
#以上是数据搬迁。下面是过滤、清理和其他转换任务。
#数据转换
# 删除DataFrame中出现的重复数据、重复行。
data=pd.DataFrame({\'k1\':[\'一\']*3+[\'二\']*4,\'K2\':[1,1,2,3,3,4,4]})
打印数据)
#Dataframe的duplicate方法返回一个布尔系列,指示每一行是否是重复行。 drop_duplicates 方法返回删除了重复行的数据帧。
打印(数据.重复())
打印(data.drop_duplicates())
# 如果只想根据K1列过滤重复项,请指定一些列进行重复检测
数据[\'v1\']=范围(7)
打印数据)
打印(data.drop_duplicates(\'k1\'))
#duplicated 和drop_duplicates 默认保留重复值的第一个组合。传递keep=last 将保留最后一个值。
print(data.drop_duplicates([\'k1\'],keep=\'last\'))
#使用函数或映射进行数据转换
#根据数组、系列或数据帧列值进行转换
data=pd.DataFrame({\'food\':[\'培根\',\'手撕猪肉\',\'培根\',\'熏牛肉\',\'咸牛肉\',\'培根\',\'熏牛肉\',\'蜂蜜火腿\',\'novalox\' ],
\'计数\':[4,3,12,6,7,8,3,5,6]})
打印数据)
#将肉写入动物映射:
Meat_to_animals={\'培根\':\'猪\',\'手撕猪肉\':\'猪\',\'熏牛肉\':\'牛\',\'咸牛肉\':\'牛\',\'蜂蜜火腿\':\'猪\',\'Novalox\': \'三文鱼\' }
印刷品(肉类和动物)
#Series: 中的映射方法可以接受包含用于修改对象的数据子集的映射关系的函数或字典对象。
data[\'animals\']=data[\'food\'].map(str. lower).map(肉类和动物)
打印数据)
#您还可以传递一个可以为您完成所有这些工作的函数。
print(data[\'food\'].map(lambda x:meat_to_animals[x.lower()]))
#重置值
#替换方法:替换
使用#fillna方法填充缺失数据可以被认为是替换的一种特殊情况,您可以一次替换单个值或多个值。
数据=pd.Series([1.-999.2.-999,-1000.3.])
打印数据)
打印(数据.替换(-999,np.nan))
打印(数据.替换([-999,-1000],np.nan))
#对不同的值进行不同的替换
print(data.replace([-999,-1000],[np.nan,0]))
#接收参数也可以是字典
打印(数据.替换({-999:np.nan,-1000:0}))
#重命名轴索引轴标签具有map方法。
data=pd.DataFrame(np.arange(12).reshape((3,4)),index=[\'OHio\',\'Colordao\',\'纽约\'],
列=[\'1\',\'2\',\'3\',\'4\'])
打印数据)
打印(data.index.map(str.upper))
#转换函数或映射以获取新对象并将其值分配给索引允许您动态修改数据帧。
data.index=data.index.map(str.upper)
打印数据)
#使用重命名创建数据集的转换版本,而不是修改原始数据
print(data.rename(index=str.title,columns=str.upper))
#rename 可以与字典对象结合来更新一些轴标签。
print(data.rename(index={\'OHIO\':\'INDIANA\'},columns={\'三\':\'peekaboo\'}))
#rename 实现复制数据框、分配其索引和列标签、修改给定数据集并传递inplace=true。
print(data.rename(index={\'OHIO\':\'INDIANA\'},inplace=True))
#离散化和分箱
#为了便于分析,连续数据通常使用Pandas 的cut 函数离散化或分割成“bin”。
年龄=[20,22,25,27,21,23,37,31,61,45,41,32]
bin=[18,25,35,60,100]
猫=pd.cut(年龄,bin)
打印(猫)
#pandas 返回一个特殊的类别对象,其中包含表示各种类别名称和用于标记年龄数据的属性的数组。
打印(猫.类别)
打印(猫.代码)
#哪一边是封闭端可以用right=Fslse来改变。
print(pd.cut(年龄,[18,26,36,61,100],right=False))
#设置自己的人脸元素名称:
group_names=[\'青年\',\'青年\',\'中年\',\'老年\']
打印(pd.cut(年龄,垃圾箱,标签=组名))
将#label 选项设置为列表或数组。如果将多个bin 而不是精确的bin 边界传递给Cut,则会根据数据的最小值和最大值计算等长度的bin。
数据=np.random.randn(20)
打印(pd.cut(数据,4,精度=2))
#将均匀分布的数据分为4组,
# qcut函数:根据样本分位数将数据分为bin。由于qcut 使用样本分位数,因此您基本上可以获得相同大小的bin(尽管cut 可能无法使每个bin 相同,具体取决于样本分位数的分布)。数据包含相同数量的数据点)
数据=np.random.randn(1000)
猫=pd.qcut(数据,4)
打印(猫)
打印(pd.value_counts(猫))
#设置自定义分位数:
打印(pd.qcut(数据,[0,0.1,0.5,0.9,1]))
#在聚合和分组操作时再次使用两个离散化函数Cut和qcut。
#检测并过滤异常值,以确定是否存在异常值(outliers)。
打印(np.random.seed(12345))
数据=pd.DataFrame(np.random.randn(1000,4))
打印(数据.描述())
#查找绝对值大于3的列中的值。
列=数据[3]
打印(列[np.abs(col3)])
#选择包含“值大于3或-3”的所有行:
打印(数据[(np.abs(数据)3).any(1)])
#将值限制在-3到3的范围内:
数据[np.abs(数据)3]=np.sign(数据)*3
打印(数据.描述())
#np.sign 将此ufunc 作为1 和-1 的序列返回,表示原始值的符号。
#排列和随机抽样
#numpy.random.permutation 函数:排列Series和Dataframe的列
df=pd.DataFrame(np.arange(5*4).reshape(5,4))
打印(df)
采样器=np.random.permutation(5)
打印(采样器)
#Permutation(5): 需要排列的轴的长度。该数组可以与基于ix 的索引操作或take 函数一起使用。
打印(df)
打印(df.take(采样器))
#选择一个随机的、有限的子集(无替换)
打印(df.take(
np.random.permutation(len(df))[:3]))
#用替换的方式产生样本:
bag=np.array([5,7,-1,6,4])
print(bag)
sampler=np.random.randn(0,len(bag))
print(sampler)
#计算指标/哑变量
#将分类变量(categorical)转换为\"哑变量矩阵(dummY matrix)\"或\"指标矩阵(Indicator matrix)
df=pd.DataFrame({\"key\":[\"b\",\"b\",\"a\",\"c\",\"a\",\"b\"],\"datal\":range(6)})
print(df)
print(pd.get_dummies(df[\"key\"]))
#给Dataframe的列加上一个前缀,以便能够跟其他数据进行合并
dummies=pd.get_dummies(df[\"key\"],prefix=\"key\")
print(dummies)
df_with_dummy=df[[\"datal\"]].join(dummies)
print(df_with_dummy)
#字符串操作字符串对象的内置方法
val=\"a,b,guido\"
print(val.split(\",\"))
pieces=[x.strip() for x in val.split(\",\")]
print(pieces)
first,second,third=pieces
print(first+\"::\"+second+\"::\"+third)
print(\"::\".join(pieces))
#find找不到返回-1,index找不到引发一个异常
print(\"guido\" in val)
print(val.index(\",\"))
print(val.find(\":\"))
#传入空字符串常常用于删除模式
print(val.count(\",\"))
print(val.replace(\",\",\"::\"))
print(val.replace(\",\",\"\"))
print(val)
#正则表达式(regex)
#提供了一种灵活的在文本中搜索或者匹配字符串模式的方法,python内置的re模块负责对字符串应用正则表达式
#re模块的函数分为三个大类:模式匹配,替换,拆分
import re
text=\"foo bar\\t baz \\ tqux\"
print(text)
print(re.split(\"\\s+\",text))
regex=re.compile(\"\\s+\")
print(regex.split(text))
#描述一个或者多个空白符的regex是\\s+,调用re.split(\"\\s+\",text)时,正则表达式会先被编辑,然后再在text上调用其他split方法
#可以用re.compile自己编译一个regex,以得到一个可重用的regex对象,如上所述,如果打算对许多字符串应用同一条正则表达式,强烈建议通过这种方法,可以节省大量的cpu时间,得到匹配的regex的所有模式
print(regex.findall(text))
# finddall:返回字符串中所有的匹配项
#search:只返回第一个匹配项
#match:只匹配字符串的首部
#pandas中矢量化的字符串函数
#通过data.map,所有字符串和正则表达式方法都能被应用于各个值,但是如果存在NA就会报错,为了解决这个问题,Series有一些能够跳过NA值的字符串操作方法,通过Series的str属性即可访问这些方法
data={\"Dave\":\"dave@google.com\",\"Steve\":\"steve@gmail.com\",\"Rob\":\"rob@gmail.com\",\"Wes\":np.nan}
print(pd.Series(data))
print(data.isnull())
print(data.str.contains(\"gmail\"))
#对字符串进行子串截取
print(data.str[:5])
#数据聚合与分组运算
#对数据集进行分组并对各组应用一个函数,再将数据集准备好之后,通常的任务就是计算分组统计或者生成透视表,pandas提供了一个灵活高效的groupby功能:利用任何可以接受pandas对象或者Numpy数组的函数
#groupby技术:
#分组运算:split(拆分)——>apply(应用)——>combine(合并)
#分组键的形式:
#列表或者数组,其长度与待分组的轴一样
#表示Dataframe某个列名的值
#字典或者Series,给出待分组轴上的值与分组名之间的对应关系
#函数,用于处理轴索引或者索引中的各个标签
df=pd.DataFrame({\"key1\":[\"a\",\"b\",\"b\",\"b\",\"a\"],\"key2\":[\"one\",\"two\",\"one\",\"two\",\"one\"],\"data1\":np.random.randn(5),\"data2\":np.random.randn(5)})
print(data)
grouped=df[\"datal\"].groupby(df[\"key1\"])
print(grouped)
#访问data1,并根据key1调用groupby
#变量grouped是一个GroupBy对象,它实际上还没有进行任何计算,只是含有一些有关分组键df[\"key1\"]的中间数据
#例如,调用GroupBy的mean方法来计算分组平均值
print(grouped.mean())
means=df[\"datal\"].groupby([df[\"key1\"],df[\"df[\"key2\"]]).mean()
print(means)
#Series根据分组键进行了聚合,产生了一个新的Series,其索引为key1列中的唯一值,通过两个键对数据进行了分组后,得到的Series具有一个层次化索引
print(means.unstack())
#分组键可以时任何长度适当的数组:
states=np.array([\"ohio\",\"california\",\"california\",\"ohio\",\"ohio\"])
years=np.array([2005,2005,2006,2005,2006])
df[\"datal\"].groupby([states,years]).mean()
#将列名用作分组键:
print(df.groupby(\"key1\").mean())
print(df.groupby([\"key1\",\"key2\"]).mean())
#Groupby 的Series方法返回一个含有分组大小的Series
df.groupby([\"key1\",\"key2\"]).size()
#对分组进行迭代 :Groupby对象支持迭代,可以产生一个一组二元元组(由分组名和数据块组成)
for name,group in df.groupby(\"key1\"):
print(name)
print(group)
for(k1,k2),group in df.groupby([\"key1\",\"key2\"]):
print(k1,k2)
print(group)
#对于多重键,元组的第一个元素将会是由键值组成的元组,对于数据片段进行操作,如将这些数据片段做成一个字典
pieces=dict(list(df.groupby(\"key1\")))
#groupby默认在axis=0进行分组,通过设置可以在其它任何轴上进行分组,如可以根据dtype对列进行分组
print(df.dtypes)
grouped=df.groupby(df.dtype,axis=1)
dict(list(grouped))
#选取一个或一组列
#对于 由Dataframe 产生的Groupby对象,用一个或一组(单个字符串或者字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的
print(df.groupby(\"key1\")[\"datal\"])
print(df.groupby(\"key1\")[\"data2\"])
print(df[\"data1\"].groupby(df[\"key1\"]))
print(df[[\"data2\"]].groupby(df[\"key1\"]))
#例如,对部分列进行聚合:计算data2列的平均值并以Dataframe形式得到结果
print(df.groupby([\"key1\",\"key2\"])[[\"data2\"]].mean())
#返回一个已分组的Dataframe(传入的是列表或者数据)或者Series(传入的是标量形式的单个列名)
s_grouped=df.groupby([\"key1\",\"key2\"])[\"data2\"]
print(s_grouped.mean())
#通过字典或者Series进行分组,除数组外分组信息还可以其他形式存在
people=pd.DataFrame(np.random.randn(5,5),columns=list(\"abcde\"),index=[\"Joe\",\"Steve\",\"Wes\",\"Jim\",\"Travis\"])
people.loc[2:3,[\"b\",\"c\"]]=np.nan
print(people)
#根据分组计算列的sum:
mapping={\"a\":\"red\",\"b\":\"red\",\"c\":\"blue\",\"d\":\"blue\",\"e\":\"red\",\"f\":\"orange\"}
by_ccolumns=people.groupby(mapping,axis=1)
print(by_ccolumns.sum())
#将mapping这个字典传给groupby即可,用Series作为分组键
map_series=pd.Series(mapping)
print(map_series)
people.groupby(map_series,axis=1).sum()
#这里Series可以被看作一个固定大小的映射,pandas 会检查Series 以确保其索引根分组轴时对齐的
#通过函数进行分组
#任何被当作分组键的函数都会在各个索引值上被调用一次,其返回值就会被用作分组名称
print(people.groupby(len).sum())
#将函数跟数组,列表,字典,Series混合使用(任何东西最终都会被转换为数组):
key_list=[\"one\",\"one\",\"one\",\"two\",\"two\"]
print(people.groupby([len,key_list]).min())