我们说我有类别,1到10,我想将red
分配给值3到5,green
分配给1,6和7,以及{{1 }到2,8,9和10。
我该怎么做?如果我试试
blue
我收到错误:df.cat.rename_categories(['red','green','blue'])
但如果我把它放在
ValueError: new categories need to have the same number of items than the old categories!
我会收到错误消息,说明存在重复值。
我能想到的唯一另一种方法是编写一个for循环,它将遍历值的字典并替换它们。是否有更优雅的解决方案?
答案 0 :(得分:6)
不确定优雅,但是如果你制作一个旧到新类别的词典,就像(注意添加的“紫色”):
>>> m = {"red": [3,4,5], "green": [1,6,7], "blue": [2,8,9,10], "purple": [11]}
>>> m2 = {v: k for k,vv in m.items() for v in vv}
>>> m2
{1: 'green', 2: 'blue', 3: 'red', 4: 'red', 5: 'red', 6: 'green',
7: 'green', 8: 'blue', 9: 'blue', 10: 'blue', 11: 'purple'}
您可以使用它来构建新的分类系列:
>>> df.cat.map(m2).astype("category", categories=set(m2.values()))
0 green
1 blue
2 red
3 red
4 red
5 green
6 green
7 blue
8 blue
9 blue
Name: cat, dtype: category
Categories (4, object): [green, purple, red, blue]
如果您确定在列中可以看到所有分类值,则不需要categories=set(m2.values())
(如果您关心分类排序,则需要订购的等效值)。但是在这里,如果我们不这样做,我们就不会在生成的分类中看到purple
,因为它是根据实际看到的类别构建的。
当然,如果您已经建立了列表['green','blue','red', etc.]
,那么只需使用它直接创建新的分类列并完全绕过此映射即可。
答案 1 :(得分:3)
似乎pandas.explode
(July 18, 2019)
发行的pandas-0.25.0
恰好适合那里,因此避免了任何循环-
# Mapping dict
In [150]: m = {"red": [3,4,5], "green": [1,6,7], "blue": [2,8,9,10]}
In [151]: pd.Series(m).explode().sort_values()
Out[151]:
green 1
blue 2
red 3
red 4
red 5
green 6
green 7
blue 8
blue 9
blue 10
dtype: object
因此,结果是一个熊猫系列,其中包含values:index
中所有必需的映射。现在,根据用户需求,我们可以直接使用它,也可以根据需要使用dict或series,交换索引和值等不同格式。让我们也来探索它们。
# Mapping obtained
In [152]: s = pd.Series(m).explode().sort_values()
1)输出为dict:
In [153]: dict(zip(s.values, s.index))
Out[153]:
{1: 'green',
2: 'blue',
3: 'red',
4: 'red',
5: 'red',
6: 'green',
7: 'green',
8: 'blue',
9: 'blue',
10: 'blue'}
2)按系列输出:
In [154]: pd.Series(s.index, s.values)
Out[154]:
1 green
2 blue
3 red
4 red
5 red
6 green
7 green
8 blue
9 blue
10 blue
dtype: object
答案 2 :(得分:2)
好的,这稍微简单一些,希望能激发更多的对话。
OP的示例输入:
>>> my_data = {'numbers': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
>>> df = pd.DataFrame(data=my_data)
>>> df.numbers = df.numbers.astype('category')
>>> df.numbers.cat.rename_categories(['green','blue','red', 'red', 'red'
>>> 'green', 'green', 'blue', 'blue' 'blue'])
这将产生ValueError: Categorical categories must be unique
作为OP状态。
我的解决方案:
# write out a dict with the mapping of old to new
>>> remap_cat_dict = {
1: 'green',
2: 'blue',
3: 'red',
4: 'red',
5: 'red',
6: 'green',
7: 'green',
8: 'blue',
9: 'blue',
10: 'blue' }
>>> df.numbers = df.numbers.map(remap_cat_dict).astype('category')
>>> df.numbers
0 green
1 blue
2 red
3 red
4 red
5 green
6 green
7 blue
8 blue
9 blue
Name: numbers, dtype: category
Categories (3, object): [blue, green, red]
强制您写出完整的字典,其中将旧类别与新类别进行1:1映射,但可读性强。然后,转换非常简单:逐行使用df.apply(在数据系列上使用.apply时隐式)获取每个值,并将其替换为remap_cat_dict中的适当结果。然后将结果转换为类别并覆盖该列。
我几乎遇到了这个确切的问题,我想创建一个新的列,从旧列转换过来的类别较少,这在这里同样容易(并且有益的是不涉及覆盖当前列):
>>> df['colors'] = df.numbers.map(remap_cat_dict).astype('category')
>>> print(df)
numbers colors
0 1 green
1 2 blue
2 3 red
3 4 red
4 5 red
5 6 green
6 7 green
7 8 blue
8 9 blue
9 10 blue
>>> df.colors
0 green
1 blue
2 red
3 red
4 red
5 green
6 green
7 blue
8 blue
9 blue
Name: colors, dtype: category
Categories (3, object): [blue, green, red]
编辑5/2/20:用df.numbers.apply(lambda x: remap_cat_dict[x])
进一步简化df.numbers.map(remap_cat_dict)
(感谢@JohnE)
答案 3 :(得分:2)
我当然在这里看不到@DSM的原始答案有问题,但是对于某些人来说,字典理解可能不是最容易阅读的内容(尽管在Python中这是一种相当标准的方法)。
如果您不想使用字典理解,但愿意使用numpy
,那么我建议您使用np.select
,它与@DSM的答案大致一样简洁,但阅读起来可能更直接,就像@ vector07的答案一样。
import numpy as np
number = [ df.numbers.isin([3,4,5]),
df.numbers.isin([1,6,7]),
df.numbers.isin([2,8,9,10]),
df.numbers.isin([11]) ]
color = [ "red", "green", "blue", "purple" ]
df.numbers = np.select( number, color )
输出(请注意,这是一个字符串或对象列,但是您当然可以使用astype('category')
轻松地转换为类别:
0 green
1 blue
2 red
3 red
4 red
5 green
6 green
7 blue
8 blue
9 blue
基本上是同一件事,但是您也可以使用np.where
来做到这一点:
df['numbers2'] = ''
df.numbers2 = np.where( df.numbers.isin([3,4,5]), "red", df.numbers2 )
df.numbers2 = np.where( df.numbers.isin([1,6,7]), "green", df.numbers2 )
df.numbers2 = np.where( df.numbers.isin([2,8,9,10]), "blue", df.numbers2 )
df.numbers2 = np.where( df.numbers.isin([11]), "purple", df.numbers2 )
这不会像np.select
那样有效,这可能是执行此操作的最有效方法(尽管我没有计时),但是可以说它更具可读性,因为您可以将每个键/值对在同一行上。
答案 4 :(得分:0)
可以这样:
import pandas as pd
df = pd.DataFrame(range(1, 11), columns=['colors'])
color2cod = {"red": [3,4,5], "green": [1,6,7], "blue": [2,8,9,10]}
cod2color = {cod: k for k, cods in color2cod.items() for cod in cods }
df['m'] = df.colors.map(cod2color.get)
df.m = df.m.astype('category')
print('---')
print(df.m.cat.categories)
print('---')
print(df.info())
答案 5 :(得分:0)
我知道这不是问题的确切答案,但我在搜索我的问题时遇到了这个问题,并认为它可能对某人有所帮助。
问题是,在这里您知道要替换为 1 分类的所有值,但我的问题与性别有关,我想要男性、女性和其他但它包含男性、女性和十几个“其他”性别。您如何为所有其他值赋予“其他”的分类类型?
请注意,这不是我的答案,我在这里找到的: Conditionally create an "Other" category in categorical column 答案发布者:user12705352 但我会把它贴在下面。
# Get a list of the top 10 neighborhoods
top10 = df['NEIGHBORHOOD'].value_counts()[:10].index
# At locations where the neighborhood is NOT in the top 10,
# replace the neighborhood with 'OTHER'
df.loc[~df['NEIGHBORHOOD'].isin(top10), 'NEIGHBORHOOD'] = 'OTHER'
#Create categorical
df['NEIGHBORHOOD'] = df['NEIGHBORHOOD'].astype(pd.CategoricalDtype(categories=df['NEIGHBORHOOD'].unique(),ordered=False))
答案 6 :(得分:0)
(问这个问题已经有一段时间了。我是数据科学的新手,所以如果我的解决方案不符合要求,请原谅我。)
我认为,更简单的方法是编写一个函数,然后将其映射到系列中。
def color(num):
blue = [2,8,9,10]
green = [1,6,7]
red = [3,4,5]
if num in blue:
return 'blue'
if num in green:
return 'green'
else:
return 'red'
df.m2 = df.m1.apply(color)