Como alterar os nomes das colunas do quadro de dados no pyspark?

201

Eu venho do fundo do pandas e estou acostumado a ler dados de arquivos CSV em um dataframe e depois simplesmente alterar os nomes das colunas para algo útil usando o comando simples:

df.columns = new_column_name_list

No entanto, o mesmo não funciona em quadros de dados pyspark criados usando o sqlContext. A única solução que eu poderia descobrir para fazer isso facilmente é a seguinte:

df = sqlContext.read.format("com.databricks.spark.csv").options(header='false', inferschema='true', delimiter='\t').load("data.txt")
oldSchema = df.schema
for i,k in enumerate(oldSchema.fields):
  k.name = new_column_name_list[i]
df = sqlContext.read.format("com.databricks.spark.csv").options(header='false', delimiter='\t').load("data.txt", schema=oldSchema)

Isso basicamente define a variável duas vezes e infere o esquema primeiro, depois renomeia os nomes das colunas e carrega o quadro de dados novamente com o esquema atualizado.

Existe uma maneira melhor e mais eficiente de fazer isso, como fazemos nos pandas?

Minha versão do Spark é 1.5.0

Shubhanshu Mishra
fonte

Respostas:

334

Há muitas maneiras de fazer isso:

  • Opção 1. Usando selectExpr .

    data = sqlContext.createDataFrame([("Alberto", 2), ("Dakota", 2)], 
                                      ["Name", "askdaosdka"])
    data.show()
    data.printSchema()
    
    # Output
    #+-------+----------+
    #|   Name|askdaosdka|
    #+-------+----------+
    #|Alberto|         2|
    #| Dakota|         2|
    #+-------+----------+
    
    #root
    # |-- Name: string (nullable = true)
    # |-- askdaosdka: long (nullable = true)
    
    df = data.selectExpr("Name as name", "askdaosdka as age")
    df.show()
    df.printSchema()
    
    # Output
    #+-------+---+
    #|   name|age|
    #+-------+---+
    #|Alberto|  2|
    #| Dakota|  2|
    #+-------+---+
    
    #root
    # |-- name: string (nullable = true)
    # |-- age: long (nullable = true)
  • Opção 2. Usando withColumnRenamed , observe que esse método permite "substituir" a mesma coluna. Para Python3, substitua xrangepor range.

    from functools import reduce
    
    oldColumns = data.schema.names
    newColumns = ["name", "age"]
    
    df = reduce(lambda data, idx: data.withColumnRenamed(oldColumns[idx], newColumns[idx]), xrange(len(oldColumns)), data)
    df.printSchema()
    df.show()
  • Opção 3. Usando o alias , no Scala você também pode usar como .

    from pyspark.sql.functions import col
    
    data = data.select(col("Name").alias("name"), col("askdaosdka").alias("age"))
    data.show()
    
    # Output
    #+-------+---+
    #|   name|age|
    #+-------+---+
    #|Alberto|  2|
    #| Dakota|  2|
    #+-------+---+
  • Opção 4. Usando sqlContext.sql , que permite usar consultas SQL em DataFramesregistradas como tabelas.

    sqlContext.registerDataFrameAsTable(data, "myTable")
    df2 = sqlContext.sql("SELECT Name AS name, askdaosdka as age from myTable")
    
    df2.show()
    
    # Output
    #+-------+---+
    #|   name|age|
    #+-------+---+
    #|Alberto|  2|
    #| Dakota|  2|
    #+-------+---+
Alberto Bonsanto
fonte
1
Eu fiz isso com um forlaço + withColumnRenamed, mas a sua reduceopção é muito bom :)
Felipe Gerard
1
Bem, como nada é feito no Spark até que uma ação seja chamada no DF, é apenas um código menos elegante ... No final, o DF resultante é exatamente o mesmo!
Felipe Gerard
2
@FelipeGerard Por favor, verifique este post , coisas ruins podem acontecer se você tiver muitas colunas.
Alberto Bonsanto 3/11
1
@AlbertoBonsanto Como selecionar coluna como aliás se houver mais de 100 colunas que é a melhor opção
3
@NuValue, você deve primeiro executarfrom functools import reduce
joaofbsm
168
df = df.withColumnRenamed("colName", "newColName")
       .withColumnRenamed("colName2", "newColName2")

Vantagem de usar desta maneira: Com uma lista longa de colunas, você gostaria de alterar apenas alguns nomes de colunas. Isso pode ser muito conveniente nesses cenários. Muito útil ao unir tabelas com nomes de colunas duplicados.

Pankaj Kumar
fonte
existe uma variante dessa solução que deixa todas as outras colunas inalteradas? com este método, e outros, apenas as colunas explicitamente nomeados permaneceu (todos os outros removido)
Quetzacoatl
1
O +1 funcionou bem para mim, apenas editou a coluna especificada, deixando outras inalteradas e nenhuma coluna foi removida.
M18.p
2
@Quetzalcoatl Este comando parece alterar apenas a coluna especificada enquanto mantém todas as outras colunas. Assim, um grande comando para mudar o nome apenas uma das potencialmente muitos nomes de coluna
user989762
@ user989762: acordado; meu entendimento inicial estava incorreto neste ...!
Quetzalcoatl
61

Se você deseja alterar todos os nomes de colunas, tente df.toDF(*cols)

user8117731
fonte
5
essa solução é a mais próxima de df.columns = new_column_name_list pelo OP, tanto em como é concisa quanto em sua execução.
Quetzalcoatl
Eu acho que isso deve ser selecionado como a melhor resposta
HanaKaze 24/01
Para mim, eu estava obtendo os nomes de cabeçalho de um dataframe de pandas, então apenas useidf = df.toDF(*my_pandas_df.columns)
Nic Scozzaro
Essa resposta me confunde. Não deveria haver um mapeamento de nomes de colunas antigas para novos nomes? Isso funciona colssendo os novos nomes de coluna e apenas assumindo que a ordem dos nomes colscorresponde à ordem das colunas do quadro de dados?
rbatt 23/06
47

Caso você queira aplicar uma transformação simples em todos os nomes de colunas, este código faz o truque: (Estou substituindo todos os espaços por sublinhado)

new_column_name_list= list(map(lambda x: x.replace(" ", "_"), df.columns))

df = df.toDF(*new_column_name_list)

Obrigado a @ user8117731 pelo toDftruque.

pbahr
fonte
13

Se você deseja renomear uma única coluna e manter o restante como está:

from pyspark.sql.functions import col
new_df = old_df.select(*[col(s).alias(new_name) if s == column_to_change else s for s in old_df.columns])
Ratul Ghosh
fonte
13

df.withColumnRenamed('age', 'age2')

Sahan Jayasumana
fonte
1
A resposta de Pankaj Kumar e resposta de Alberto Bonsanto (que são a partir de 2016 e 2015, respectivamente) já sugerem usando withColumnRenamed.
Andrew Myers
Obrigado, sim, mas existem algumas sintaxes diferentes, talvez devêssemos coletá-las em uma resposta mais formal? data.withColumnRenamed (oldColumns [idx], newColumns [idx]) vs data.withColumnRenamed (columnname, novo columnname) Eu acho que depende de qual versão do pyspark o seu uso
Sahan Jayasumana
1
Esta não é uma sintaxe diferente. A única diferença é que você não armazenou os nomes das colunas em uma matriz.
Ed Bordin
13

esta é a abordagem que eu usei:

criar sessão pyspark:

import pyspark
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName('changeColNames').getOrCreate()

criar quadro de dados:

df = spark.createDataFrame(data = [('Bob', 5.62,'juice'),  ('Sue',0.85,'milk')], schema = ["Name", "Amount","Item"])

veja df com nomes de colunas:

df.show()
+----+------+-----+
|Name|Amount| Item|
+----+------+-----+
| Bob|  5.62|juice|
| Sue|  0.85| milk|
+----+------+-----+

crie uma lista com novos nomes de coluna:

newcolnames = ['NameNew','AmountNew','ItemNew']

altere os nomes das colunas do df:

for c,n in zip(df.columns,newcolnames):
    df=df.withColumnRenamed(c,n)

visualize o df com novos nomes de coluna:

df.show()
+-------+---------+-------+
|NameNew|AmountNew|ItemNew|
+-------+---------+-------+
|    Bob|     5.62|  juice|
|    Sue|     0.85|   milk|
+-------+---------+-------+
Grant Shannon
fonte
9

Fiz uma função fácil de usar para renomear várias colunas para um dataframe pyspark, caso alguém queira usá-lo:

def renameCols(df, old_columns, new_columns):
    for old_col,new_col in zip(old_columns,new_columns):
        df = df.withColumnRenamed(old_col,new_col)
    return df

old_columns = ['old_name1','old_name2']
new_columns = ['new_name1', 'new_name2']
df_renamed = renameCols(df, old_columns, new_columns)

Cuidado, as duas listas devem ter o mesmo comprimento.

Manrique
fonte
1
Bom trabalho neste. Um pouco de exagero pelo que eu precisava. E você pode simplesmente passar o df porque old_columnsseria o mesmo que df.columns.
Darth Egregious
6

Outra maneira de renomear apenas uma coluna (usando import pyspark.sql.functions as F):

df = df.select( '*', F.col('count').alias('new_count') ).drop('count')
scottlittle
fonte
3

Eu uso este:

from pyspark.sql.functions import col
df.select(['vin',col('timeStamp').alias('Date')]).show()
Mike
fonte
2
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
Isma
1

Você pode usar a seguinte função para renomear todas as colunas do seu quadro de dados.

def df_col_rename(X, to_rename, replace_with):
    """
    :param X: spark dataframe
    :param to_rename: list of original names
    :param replace_with: list of new names
    :return: dataframe with updated names
    """
    import pyspark.sql.functions as F
    mapping = dict(zip(to_rename, replace_with))
    X = X.select([F.col(c).alias(mapping.get(c, c)) for c in to_rename])
    return X

Caso você precise atualizar apenas os nomes de algumas colunas, você pode usar o mesmo nome na lista replace_with

Para renomear todas as colunas

df_col_rename(X,['a', 'b', 'c'], ['x', 'y', 'z'])

Para renomear algumas colunas

df_col_rename(X,['a', 'b', 'c'], ['a', 'y', 'z'])
Clock Slave
fonte
0

Para uma única renomeação de coluna, você ainda pode usar toDF (). Por exemplo,

df1.selectExpr("SALARY*2").toDF("REVISED_SALARY").show()
ganeiy
fonte
0

Podemos usar várias abordagens para renomear o nome da coluna.

Primeiro, vamos criar um DataFrame simples.

df = spark.createDataFrame([("x", 1), ("y", 2)], 
                                  ["col_1", "col_2"])

Agora vamos tentar renomear col_1 para col_3. PFB algumas abordagens para fazer o mesmo.

# Approach - 1 : using withColumnRenamed function.
df.withColumnRenamed("col_1", "col_3").show()

# Approach - 2 : using alias function.
df.select(df["col_1"].alias("col3"), "col_2").show()

# Approach - 3 : using selectExpr function.
df.selectExpr("col_1 as col_3", "col_2").show()

# Rename all columns
# Approach - 4 : using toDF function. Here you need to pass the list of all columns present in DataFrame.
df.toDF("col_3", "col_2").show()

Aqui está a saída.

+-----+-----+
|col_3|col_2|
+-----+-----+
|    x|    1|
|    y|    2|
+-----+-----+

Eu espero que isso ajude.

neeraj bhadani
fonte