Kr的pandas技巧笔记

最近在做一个数据集的可视化项目,又捡起了pandas和python,这里把实际用到的技巧干货写下来,防止忘记再次去网上各种查和看documentation,同时也分享给大家.

1. 找出nan项

DataFrame.isnull()

返回一个大小和 store_items 一样的布尔型 DataFrame,并用 True 表示具有 NaN 值的元素,用 False 表示非 NaN 值的元素。
2. 统计nan项数量

Series.isnull().sum()

3. 看单个元素是不是nan:因为pandas里面的nan都指向np.nan这样一个object,np.nan本身的type是float。python中is函数的作用是比较object的id

element is np.nan

4. unique() 和 nunique() 用来获取去除了重复值的列(或去重列中的元素数目)
注意:unique是series的方法,如果想在dataframe中实现类似功能,使用df.drop_duplicates(subset=[‘col_name’],keep=’last’)
keep=’last’的意思就是保留重复行里面的最后一行。subset的意思是,按照这一列的重复情况来去重。
顺便一提,df.duplicated(subset=[‘col_name’],keep=’last’)方法是返回除了你要keep的那一行都为True。要实现和drop_duplicates一样的切片功能可以:df[~df.duplicated(subset=[‘col_name’],keep=’last’)]

>>> df['generation'].unique()
array(['Generation X', 'Silent', 'G.I. Generation', 'Boomers',
       'Millenials', 'Generation Z'], dtype=object)
>>> df['country'].nunique()
101

对每个元素应用函数,function中应该有return

DataFrame/Series.apply(function)

5. #%%是spyder里面代码块的标识

6. Pandas里的assign方法
assign方法可以直接给 DataFrame添加一列,或者替换掉名字相同的列;
特别注意在assign里面使用lambda函数时,lambda函数的变量是该DataFrame

DataFrame.assign(column_name=[])

7. Pandas里的map apply applymap方法
区别:

   a
0  1
1  2
2  3
def add_num(a,b):
    return a+b
b = a['a'].apply(add_num, args=(3,))
     a
0    4
1    5
2    6

8. Pandas怎样替换数值
可以使用replace方法,也可以用map,apply等方法;
区别在于处理没有映射到的value时前者返回原value,后者返回NaN,并且速度更快。
记住两者区别,以及用字典映射来替换就可以了。

data.replace({"gender":{'1':'男', '0':'女'}})
data['gender']=data['gender'].map({'1':'男', '0':'女'})

9. Pandas根据条件来进行替换
笔者遇到了一个情况,将b列中的NaN值根据a和d的对应的关系来替换,具体而言就是把b列的NaN转化为d列中的9,因为9和a列里的3是对应关系。
也就是说,将一列中的特定元素根据另外两列的对应关系进行替换

   a    b    c  d
0  1  4.0  7.0  1
1  2  5.0  8.0  2
2  3  NaN  NaN  9

这一步基于上面替换数值的方法,可以使用replace或者map方法来做,参数为dict。
首先把对应关系转化为字典,再把b列中的NaN变成a列中对应得值,最后根据a列与d列的对应关系,把b列的值转化。
为什么这里要用map,不用replace,因为map没有找到返回的是NaN,而replace返回a列中的原值,我们并不需要。
这样的另一好处是,原index不会改变。

cond = df.b.isnull()
replacement = dict(zip(df.a,df.d))
df.loc[cond,'b'] = df.loc[cond,'a'].map(replacement)

10. Pandas里面分列的方法
如果一列里面的元素是str,那么用str方法来切分

df['name'].str.split(';',expand=True

如果是元组,那么使用apply来得到一个新的dataframe,很神奇

df['a'].apply(pd.Series)

合并列

df['ab'] = df[['a', 'b']].apply(tuple, axis=1)

11. pandas里面进行列循环的方法
一个iterrow,一个是itertuple
iterrow返回index和包含一行数据的series,以列名为index
itertuple返回一个元组,包含行数据;
显然后者比前者快

12. 一个关于条件判断的现象,有如下DataFrame
a = pd.DataFrame()
a = a.assign(a=[1,2,3],b=[4,5,np.nan],c=[7,8,np.nan],d=[1,2,3])

   a    b    c  d
0  1  4.0  7.0  1
1  2  5.0  8.0  2
2  3  NaN  NaN  3

以下五个条件判断
4. a[‘b’].isnull() & a[‘c’].isnull() 正常运行,在索引2得到True
5. a[‘b’].isnull() & a[‘a’]==3 无法得到预期结果True,在索引2得到了False
6. (a[‘b’].isnull()) & (a[‘a’]==3) 在各条件项上加上括号以后就得到了正确答案
7. a[‘d’]==2 & a[‘a’]==2 报错(ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().)
8. (a[‘d’]==2) & (a[‘a’]==2) 正常运行并得到预期结果。
原因是运算符的优先级问题,&的优先级更高

13. excel删除空行的方法
ctrl+g,选择blank

14. Pandas切片方法
对于DataFrame可以使用.loc方法,也可以使用iloc,
前者可以用列名取值,后者只能用行列数

   a    b    c  d
0  1  4.0  7.0  1
1  2  5.0  8.0  2
2  3  NaN  NaN  9

slice = df.loc[1:2,['a','b']]
slice = df.iloc[1:,0:1]

df['a']
df.a

当进行多重切片时不建议叠加使用直接法,会弹出警告,而转而使用loc或者iloc。

15. 方法链
这个用好了可以让代码变得很整洁,一定要用。大概意思就是可以对一个df或者series链式使用方法,需要注意的是,如果想像我这样每个方法提一行,就要给外面加个括号。

df = (df.dropna()
        .unique()
        .loc[1,'a']
        .map(lambda element: element+1 if element <5
             else element-1))

16. 匿名函数
上面一条里面的lambda函数就是匿名函数,作用相当于一个不用def的函数,放在map,apply里面相当好用。lambda后面是传入参数的名字。注意lambda里面不能赋值。所以复杂一点的func还是要def一下。

17.指定数据类型
读取csv时指定特定列的数据类型,这样可以避免”01000″这类编号被读取为数字变成”1000″类似的情况

df = pd.read_csv("somefile.csv", dtype = {'column_name' : str})

18. 常见caveat

SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html

避免方法:

df2 = df[['A']]
df2['A'] = df['A'] / 2

df2 = df.loc[:, ['A']]
df2['A'] = df['A'] / 2

19. 大文件读取方法
这里我写了一个函数,使用分块读取并处理来降低内存使用量,chunk_size就是你一次循环中要读取的行数

def read_big_data(file_path, delimiter=',', function=None, chunk_size=1000000):

    reader = pd.read_csv(file_path, delimiter=delimiter, iterator=True)

    loop = True
    chunks = []
    while loop:
        try:
            chunk = reader.get_chunk(chunk_size)
            if function is not None:
                chunk = function(chunk)
                print('current_chunk_processed')
            chunks.append(chunk)
        except StopIteration:
            loop = False
            print('Iteration is stopped')
    df = pd.concat(chunks, ignore_index=True)
    return df

20. 按列条件合并df

new = pd.merge(a,b,how='left',on = '')
a.merge(...)

以下来自处理一个美国人口数据集的经验

21. groupby 和 transform

df.groupby(['A','B']).method()

返回的是带index的serial,index为’A’,’B’对应的值,如果要消除这个index,并将index还原为df中的列,可以使用:

df.groupby(['A','B']).method().reset_index()

如果只是要消除index,不还原为列,则可以:

df.groupby(['A','B']).method().reset_index(drop = True)

需要注意的是,groupby后接的方法,会把df行数会按照不同的”A”,”B”值来浓缩,使得不同的”A”,”B”对应唯一一行,如果不想浓缩,而与原df行数相同,可以使用:

df.groupby(['A','B']).transform('method')method()

经由transform返回的是一个没有index的serial。
transform可以用来计算同类别数值的占比,比如我想计算人口普查数据各城市的男女比例

'''
CITY NAME GENDER
宜宾  张三     M
宜宾  李四     F
南京  小美     F
南京  小兰     F
'''

gender_pop_city = (df.groupby(['CITY','GENDER'])
                    .NAME
                    .count()
                    .reset_index()
                    .rename(columns= {'NAME':'NUM' })
                    )
'''
CITY  GENDER  NUM
宜宾      M     1
宜宾      F     1
南京      F     2
'''

pop_city = gender_pop_city.groupby('CITY')\
                            .NUM\
                            .transform('sum')

gender_pop_city['RATIP'] = gender_pop_city['NUM']/ pop_city
'''
CITY  GENDER  NUM   RATIO
宜宾      M     1     0.5
宜宾      F     1     0.5
南京      F     2      1
'''
以下来自帮老师整理历史课程资料时使用的技巧

22. 如何在第一行插入列和信息
这里推荐新建DataFrame


first_row = pd.DataFrame(columns = data.columns)

first_row.loc['总数'] = data.apply(lambda x:sum(x) if x.dtype == bool else '', axis=0)

data = pd.concat([first_row,data])

with pd.option_context("display.max_columns", None):
    print(data)

·这个地方要注意,python中的lambda函数在任何情况下都要有return value,所以写了if,就必须要跟else来囊括所有情形,否则报错,笔者也纠结了好一会儿才发现。

·另外复习以下,对df使用apply,如果axis=0,则传入参数是每列,axis=1,则传入参数是每行。因此这里lambda函数中的x是df的列,这里只对类型是bool的列求和。

·这里用到知识点还有切片:
df.loc[ ] 只传入一个参数的时候是切index,如果没有对应index,则会append新的一行在df尾部。这里因为df是空的DataFrame,所以新的一行也在第一行。

·concat方法,不能接在df后面,只能pd.concat(),并且被concat的df们要放在一个list里面传入。

23. 找到含有合适字符串的行
使用contains函数

is_phd = data['授课对象'].str.contains('(博士)')

'''
UserWarning: This pattern is interpreted as a regular expression, and has match groups. To actually get the groups, use str.extract.

'''
原因是contains传入的参数是正则表达式,括号在正则表达式中是有含义的,可以无视。

24. 不同的DataFrame输出到同一xlsx表格不同sheet

with pd.ExcelWriter('result.xlsx',
                    engine="openpyxl",
                    mode="a",
                    if_sheet_exists='replace') as writer:
    output.to_excel(writer,
                    sheet_name='主页')

    word_freq.value_counts()\
        .reset_index()\
        .to_excel(writer,
                  sheet_name = '关键词统计')

使用ExcelWriter即可,这里推荐用with来配合使用ExcelWriter,比较美观,不然就要writer.save()才能保存你的excel。
另外这里讲一下value.counts方法,很好用,对列使用,获取列中所有元素出现次数。

25. 反选条件

如代码所示,不是硕博专科,那么就是本科了,使用”~”来做”not”,让True和False转换;”&”做”和”的逻辑判断;”|”做”或”

is_master = data['授课对象'].str.contains('(硕士)|(研究生)')
is_phd = data['授课对象'].str.contains('(博士)')
is_associate = data['授课对象'].str.contains('(专科)')
is_bachelor = (~is_master) & (~is_phd) & (~is_associate)

26. Seaborn不显示中文怎么办?
此后如果从非官网看到方法,放出索引
来自https://zhuanlan.zhihu.com/p/337423390
指定字体即可

rc = {'font.sans-serif': 'SimHei',
      'axes.unicode_minus': False}
sns.set( rc=rc)

27. 读取指定列,并同时规定列的类型
我有个特别大的csv需要读取,因此只读取部分列,并同时规定data type,
只需要:

d_col = dict({'col_name0':'dtype','col_name1':'dtype'})
df = pd.read_csv('fileName', usecols=d_col, dtype=d_col)

这里用dict可以同时满足usecols和dtype两个参数的输入,在不需要指定data type的时候usecols也可以用list输入。

28.int类型的列里面有空值怎么办

d_col = dict({'HOUSEHOLD_INCOME':'int32','col_name1':'dtype'})

如果HOUSEHOLD_INCOME列中有空值,会报错,因为空值不属于int32的范畴。此时可以使用pandas数据类型”Int32″,此时空值会变成:pd.NA


df.plot()

df.plot(kind='kde')

cond = (df.col>=1) & (df.col2)

cond = df.col.between(1,2)

cond = df.col == 1|2

df.memory_usage(deep=True)
'''
YEAR                 11247312
SAMPLE               11247312
SERIAL               11247312
CBSERIAL             11247312
...

LAN_POP_RATIO        11247312
INDI                 11247312
Length: 175, dtype: int64
'''

df.info(memory_usage='deep')
'''

Int64Index: 1405914 entries, 3 to 2955667
Columns: 174 entries, YEAR to INDI
dtypes: float64(125), int32(1), int64(45), object(3)
memory usage: 1.8+ GB
'''

32. 减少内存的两个方法
第一:指定更小的数据类型
对于一个100多万行的series,默认整数类型是int64,但如果用不到范围那么大的数字可以改为int32,int16.

df['YEAR'].info()
'''

Int64Index: 1405914 entries, 3 to 2955667
Series name: YEAR
Non-Null Count    Dtype
1405914 non-null  int16
dtypes: int16(1)
memory usage: 13.4 MB
'''

第二,如果df中有大量重复文本数据,可以使用category dataType
category本质上相当于将str转化为数字,再用一个codebook把数字和str对应起来,以此节省内存空间。

test.FIP.info(memory_usage='deep')
'''

Int64Index: 1405914 entries, 3 to 2955667
Series name: FIP
Non-Null Count    Dtype
1405914 non-null  category
dtypes: category(1)
memory usage: 13.5 MB
'''

如果分类变量是有序的怎么办?我们可以指定顺序。
例如一个产品df里面,有差、良、优三种评分,则可以:

df['quality']=df.quality.astype('category',categories=['差','良','优'],ordered=True)
df.sort_values('quality')

这样如果根据quality进行排序,不再按字母顺序排序,而是会根据我们指定的顺序来排序。(默认升序,ascending=True,也就是从差->优)

33. 给重复项加上后缀
而且后缀是重复次数。

df = pd.DataFrame([['a'], ['a'], ['a'], ['b'], ['b'], ['a'],['c']],
                  columns=['A'])
'''
加之前
   A
0  a
1  a
2  a
3  b
4  b
5  a
6  c
'''
cond = df.A.duplicated(keep=False)
rep_num = df.groupby('A').cumcount()
b = df.A.mask(cond, df.A + rep_num.astype(str))
'''
加之后
0    a0
1    a1
2    a2
3    b0
4    b1
5    a3
6     c
Name: A, dtype: object
'''

34. 按特定列值分开df,按行数平均分开df

下面演示按照df厘米YEAR不同数值分成不同的sub df,储存在一个dict里面,用key来获取。
注意:YEAR列依然在sub df里面

dfs = dict(tuple(data.groupby('YEAR')))
dfs.values()
dfs.items()

平均分使用numpy.array_split(df, n)即可。无法除尽也可以分成大致相同的n份,如果你计算刚好可以平均分配,那可以用np.split(df, n)。array_split的好处就在于想分几份就几份,不会报错。

35. tqdm显示apply,transform进度条


conda install -c conda-forge tqdm

from tqdm import tqdm

tqdm.pandas(desc='anything')

data.loc[cond,'NUCLEAR'] = (data[cond]
                                .groupby('CLUSTER')
                                .RELATE
                                .progress_transform(
                                    lambda col: 0 if any((col==5)|(col==6)|(col==9)) else 1))

36. pandas多核运算
对一个两千多万行的df进行transform操作,每秒只能处理大概3600行,tqdm估计要50分钟。。。于是决定找包,dask,rapids,pandarelle 似乎我都配置成功,不行。。。算了还是自己写一个并行吧。
尝试了两种方法:
a. 把要并行运算的df拆分后分开保存,在subprocess里面分别读取:结果不可行,因为io太耗时间。代码如下:

import pandas as pd
import numpy as np
from tqdm import tqdm
import multiprocessing as mp

def func2(message):
    df = pd.DataFrame(data=np.ones([10,100000]))
    tqdm.pandas(desc=f'job{message}')
    df = df.progress_apply(lambda col: col+5)
    print('job done, now caching')
    df.to_parquet(f'df_temp{message}')
    print('caching done')
    print(df.columns)

def para2(func):
    cpu_count = mp.cpu_count()
    with mp.Pool(10) as p:
         p.map(func, [i for i in range(10)])
    print('para work done, now loading cache')
    ret_list = [pd.read_parquet(f'df_temp{i}.csv') for i in range(10)]
    print(ret_list)
    return pd.concat(ret_list)

para2(func2)

需要注意的地方是:
1 multiprocessing这个pool方法,各个进程之间并不共享内存,而且和主进程之间传data也非常慢。这里是给subprocess一个message,然后按照这个message自己去读取。如果要处理大的dataframe,我认为应该事先把dataframe切割好,保存为名字带编号的parquet,然后告诉sub_process自己去读取。
2 sub_process做完工作以后不要return你的dataframe,原因如上速度太慢,而是选择将dataframe保存,再让主进程一个一个读取,并合并。
3 这种方法进度条只能显示一个进程的,我还在寻找让多个进度条同时存在的方法,如果你知道,希望你能告诉我。
4 在df的保存读取上面,我试了几种数据类型,feather比parquet快,建议用feather。hdf比这两个慢,但有个好处是几个df可以合并到一个hdf里面,按key读取,比较美观。

b.直接传入拆分后的df到subprocess中,这样子速度更快,缺点是内存开销更大(原因未知)。
下面具体讲讲这种方法。
首先是package选择,我们使用multiprocess,而不是multiprocessing。原因在于这个方法会装饰传入func,这样会导致装饰后的func变为local variable,无法被pickle。

def parallelise_func(func,pbar=False):
    def wrapper(jobs):
        s_time = time.perf_counter()
        job_num = jobs[1]
        dataFrame = jobs[0]
        print(f'job {job_num} passed to the subprocess and is being dealt with')
        if pbar is True:
            tqdm.pandas()
        result = func(dataFrame)
        print(f'{job_num}th job done, takes {time.perf_counter()-s_time} seconds')
        return result
    print('function parallelised')
    return wrapper

def test_func(df):
    print(f'current memory usage:{psutil.virtual_memory().used/2.**30}')
    df = df.apply(lambda col: col+5)
    return df

def test(core=8):
    print('create ten 10x100000 DataFrames to test for speed \n    default core counts = 8')
    List_DataFrame = [pd.DataFrame(data=np.ones([10,100000])) for _ in range(10)]
    print(f'1 dataframe size:{List_DataFrame[0].memory_usage().sum()/2**30}')
    res = parallelise(List_DataFrame, test_func, core, pbar=True)
    return res

如代码所示,我见把要传入df.apply()或者transform里的func写好,然后用一个装饰函数parallelise_func把这个func改造为适应为多进程运行的结构。这里因为multiprocessing用的是pickle,而multiprocess用的是dill,我选择multiprocess,不然会报错。
原因在此处
更多描述

pool就相当于劳动力市场,cpu就是打工人,第一个参数是func是工作流程,第二个参数是一个list,list里面装着df,df就是工作内容。
pool有几个方法,map, imap, 区别见这里
用map就行了。子进程运行完毕以后,使用的内存会释放掉。

from datetime import datetime

def not_during_the_night(func):
    def wrapper():
        if 7  datetime.now().hour < 22:
            func()
        else:
            pass
    return wrapper

def say_whee():
    print("Whee!")

say_whee = not_during_the_night(say_whee)

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_whee():
    print("Whee!")

38. np.where v.s. pandas where; np.nan v.s. pd.NA

今天刚发现了np.where的用法,奉为天人,比apply lambda函数快多了,而且写出来的代码也特别优雅简洁。
具体用法是给np.where(condition, df1, df2)三个参数 ,条件,满足条件赋值,不满足条件赋值。
和pandas where的区别在于,没有pd.where 这样的topdown方法,按照官网说法:

df1.where(m, df2)

np.where(m, df1, df2).

这样使得使用限制很多,比如我不想要df1的值呢。所以还是使用numpy.where吧!
但也有一个问题,numpy.where无法对pd.NA进行比较,会报错:TypeError: boolean value of NA is ambiguous

注意:data = data.fillna(np.nan)没有用,只能data = data.replace({pd.NA: np.nan})具体原因不明。
另外要注意的是np.nan是float,而pd.NA是一个pandas自定义类NAType。pd.NA可以放在”Int”(注意I大写了)类col中,保持其他数值为int,一旦引入np.nan那么该列格式会被转化为float。
所以我们在读取csv的时候,空值用哪种格式,和数据类型有关,如果数据类型是整数,那么就是pd,如果是浮点数,那么就是np

所以这也是np.where的一个缺陷吧。

然而并没有这么简单,下面这个小测试显示,其实只有在pd.NA在np.array里面的时候,才会出现TypeError: boolean value of NA is ambiguous。但我在实际使用的时候使用dataframe.col>0,却出现了报错。我只能怀疑在处理大量数据(我这有两千多万行)的时候,转换为Series会转换为array去运算。


pd.NA>0
df = pd.DataFrame({'a':[pd.NA, 1, 2, 3]})
ar = np.array([pd.NA, 1, 2, 3])
sr = pd.Series([pd.NA, 1, 2, 3])
print('dataframe with pd.NA ok')
np.where(df > 1, 5, 0)
df>1
print('array with pd.NA not ok')
np.where(ar > 1, 5, 0)
ar>1
print('Series with pd.NA  ok')
np.where(sr > 1, 5, 0)
sr>1

最后解决办法:把pd.NA转化为-1.。。。。。。。。。。

39. 如何统计groupby中的分类的数量

主要有count和size两类方法
转载自https://favtutor.com/blogs/pandas-groupby-count

import pandas as pd
import numpy as np

data = {
  "Students": ["Ray", "John", "Mole", "John", "John", "John", "Ray", "Rick"],
  "Subjects": ["Maths", "Economics", "Science", "Maths", np.nan, "Statistics", "Statistics", "Computers"]
}
df = pd.DataFrame(data)

print(df.groupby('Students').size())

print(df.groupby('Students').count())

size和count的区别在于,count不会统计nan项,size会,size返回的数量加总就是原始df总行数。

如果你使用了多个key进行groupby,那么你会得到一个multiindex Series,这个时候配合.unstack()方法,可以把multiindex Series转化为一个dataFrame,level 0是index,level 1是col

40. 如何给multiindex series排序

书接上回,我此时有了新的需求,但并没有在网上找到答案,于是自己摸索了一下。
假如我们groupby两个col,并因此得到了一个multiindex series,类似于:

CLUSTER        LANGAUGE
2005000000021  1           3
2005000000041  0           1
               1           1
2005000000061  1           2
2005000000101  1           1
2005000000131  0           2
               1           4
2005000000151  1           3
               11          1
2005000000161  1           1
2005000000181  1           1
2005000000211  1           2
2005000000221  2           1
2005000000261  1           1
2005000000271  1           1
2005000000301  0           2
               1           2
2005000000311  1           1
2005000000331  0           2
               63          2
dtype: int64

我想给这个series在每个group内部按照#降序#排序怎么办?
并不能只用sort_value,因为会破坏multiindex结构

fam_lan.sort_values(ascending=False)
Out[27]:
CLUSTER        LANGUAGE
2005000000131  1           4
2005000000021  1           3
2005000000151  1           3
2005000000331  0           2
2005000000301  1           2
               0           2
2005000000211  1           2
2005000000331  63          2
2005000000131  0           2
2005000000061  1           2
2005000000151  11          1
2005000000161  1           1
2005000000041  0           1
2005000000221  2           1
2005000000261  1           1
2005000000271  1           1
2005000000101  1           1
2005000000311  1           1
2005000000041  1           1
2005000000181  1           1
dtype: int64

像是level 0 index里面的2005000000131,就和自己的两个level 1 index分开了。一个在第一行,一个在第八行。
解决办法:在sort完毕之后,进行sort_index。

fam_lan.sort_values(ascending=False).sort_index(level=0, sort_remaining=False)
Out[29]:
CLUSTER        LANGUAGE
2005000000021  1           3
2005000000041  0           1
               1           1
2005000000061  1           2
2005000000101  1           1
2005000000131  1           4
               0           2
2005000000151  1           3
               11          1
2005000000161  1           1
2005000000181  1           1
2005000000211  1           2
2005000000221  2           1
2005000000261  1           1
2005000000271  1           1
2005000000301  1           2
               0           2
2005000000311  1           1
2005000000331  0           2
               63          2
dtype: int64

观察2005000000131,已经达成了我们想要的效果,4排在1前面。注意,此处sort_index(level=0, sort_remaining=False)一定要sort_remaining=False,否则他会把level 1的index也进行排序就改变了我们之前对value排序的结果。

接下来,如果我想要每个小group里面的最大值怎么搞?
有了我们之前的排序工作就变得很简单了。

fam_lan.sort_values(ascending=False).sort_index(level=0, sort_remaining=False).groupby(level=0).head(1)
Out[32]:
CLUSTER        LANGUAGE
2005000000021  1           3
2005000000041  0           1
2005000000061  1           2
2005000000101  1           1
2005000000131  1           4
2005000000151  1           3
2005000000161  1           1
2005000000181  1           1
2005000000211  1           2
2005000000221  2           1
2005000000261  1           1
2005000000271  1           1
2005000000301  1           2
2005000000311  1           1
2005000000331  0           2
dtype: int64

问题解决!

41. pandas提速秘诀,向量化编程

def infer_immi_fam(data):

    immi_fam = (data.groupby('CLUSTER').BPL
                         .transform(lambda bpl: 1 if any(bpl>99) else 0))
    print('immigration status added.')
    return immi_fam

''' 向量化!'''
    immi_fam = data.CLUSTER[data.BPL>99]
    data['IMMIGRATION_FAM'] = np.where(data.CLUSTER.isin(immi_fam),1,0)
    return data

Original: https://blog.csdn.net/weixin_43892258/article/details/123937410
Author: 顾 Kairey
Title: Kr的pandas技巧笔记

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/750945/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球