lesson5-基础IO

1. 文件

1.1 基础概念 

  • 文件 = 文件内容 + 属性(也是数据)
  • 对文件的所有操作,无外乎是:a.对内容 b.对属性
  • 站在系统的角度,能够被input读取,或者能够output写出的设备就叫做文件
    • 狭义文件:普通的磁盘文件
    • 广义文件:显示器,键盘,网卡,声卡,显卡,磁盘,几乎所有的外设,都可以称之为文件

1.2 访问文件 

  • 访问文件的本质:是进程在访问
  • 文件在磁盘(硬件)上放着,而向硬件写入,只有操作系统才有这个权利
  • 如果普通用户,也想写入那就必须让OS提供接口----文件类的系统调用接口

1.3 关于系统接口 

  • 一旦使用系统接口,编写所谓的文件代码,就无法再其他平台上直接运行了,
  • 而像C++/Java这样的语言,它们就对文件系统接口进行了封装,包括所有访问文件的操作
  • C++对所有的平台的代码都实现一遍,通过条件编译,动态裁剪实现封装性

为什么要学习OS层的文件接口? 

  •  不同的语言,有不同的语言级别的文件访问接口(都是不一样的),但是底层都封装了系统接口
  • 系统接口只有一个,OS也只有一个,

1.4 C语言文件接口

1.4.1写入接口: fwrite fprintf fputs

  • 在fwrite写入时,传的strlen(s1),不需要加一
  • \0结尾是C语言的规定,文件不用遵守,文件要保存的是有效数据!

1.4.2 读取接口: fgets

 

  • 这里以只读的方式打开

1.5 系统文件接口

1.5.1 打开文件: open

  •  pathname表示文件名,
  • flags是一种标记,标记打开文件的状态
  • mode表示没有文件,创建文件时给的初始权限

open的第一个参数

open函数的第一个参数是pathname,表示要打开或创建的目标文件。

  • 若pathname以路径的方式给出,则当需要创建该文件时,就在pathname路径下进行创建。
  • 若pathname以文件名的方式给出,则当需要创建该文件时,默认在当前路径下进行创建。(注意当前路径的含义)

open的第二个参数

open函数的第二个参数是flags,表示打开文件的方式。

其中常用选项有如下几个

参数选项含义
O_RDONLY只读的方式打开文件
O_WRNOLY只写的方式打开文件
O_APPEND追加的方式打开文件
O_RDWR读写的方式打开文件
O_CREAT当目标文件不存在时,创建文件

 打开文件时,可以传入多个参数选项,当有多个选项传入时,将这些选项用“或”运算符隔开。
例如,若想以只写的方式打开文件,但当目标文件不存在时自动创建文件,则第二个参数设置如下:

O_WRONLY | O_CREAT

扩展: flags标记 

 

  • 系统中的 flags就是宏定义

open的第三个参数

open函数的第三个参数是mode,表示创建文件的默认权限。

例如,将mode设置为0666,则文件创建出来的权限如下:

  •  -rw-rw-rw-

但实际上创建出来文件的权限值还会受到umask(文件默认掩码)的影响,实际创建出来文件的权限为:mode&(~umask)。umask的默认值一般为0002,当我们设置mode值为0666时实际创建出来文件的权限为0664。

  • -rw-rw-r--

若想创建出来文件的权限值不受umask的影响,则需要在创建文件前使用umask函数将文件默认掩码设置为0。

umask(0); //将文件默认掩码设置为0

注意: 当不需要创建文件时,open的第三个参数可以不必设置

open的返回值 

open函数的返回值是新打开文件的文件描述符。

1.5.2 打开-读-写-关闭 : open write read close

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    umask(0);// 清空过滤权限
    
    //fopen("log.txt", "w");
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);// 写入 创建 清空
    const char* s = "hello write\n";
    //const char* s = "aa\n";
    write(fd, s, strlen(s));
    flose(fd);

    //fopen("log.txt", "a");
    int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);// 写入 创建 追加
    printf("open success, fd: %d\n", fd1);
    int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);// 写入 创建 追加
    printf("open success, fd: %d\n", fd2);
    int fd3 = open("log3.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);// 写入 创建 追加
    printf("open success, fd: %d\n", fd3);
    int fd4 = open("log4.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);// 写入 创建 追加
    printf("open success, fd: %d\n", fd4);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);

    //fopen("log.txt", "r");
    int fdd = open("log.txt", O_RDONLY); // 只读
    char buffer[64];
    memset(buffer, '\0', sizeof(buffer));
    read(fdd, buffer, sizeof(buffer));
    printf("%s", buffer);
    flose(fdd);

    return 0;
}

  •  我们在应用层看到一个很简单的动作,在系统接口层面甚至OS层面,可能要做非常多的动作!

1.6 文件描述符-fd

在进程中每打开一个文件,都会创建有相应的文件描述信息struct file,这个描述信息被添加pcb的struct files_struct中,以数组的形式进行管理,随即向用户返回数组的下标作为文件描述符,用于操作文件

这里借用一下上面程序的结果

  •  这里怎么是从3开始的呢,0 1 2 去那哪里了?
  • 一个文件打开默认会打开stdin stdout stderr(标准输入 标准输出 标准错误),它们也用0 1 2 表示

1.6.1 文件指针FILE*的认识

  •  FILE是一个struct结构体(内部有很多成员),是由C标准库提供的
  • 而在C文件库函数内部一定要调用系统调用,站在系统角度,它只认识fd,所以可以推出FILE结构体里面必定封装了fd!!!

1.6.2 证明FILE内部封装了fd

1.6.2 fd的理解

fd在内核本质是一个数组下标

  • 进程要访问文件,必须先打开文件
  • 文件要被访问,前提是加载到内存中,才能被直接访问
  • 一般而言 进程 : 打开的文件 = 1 : n,但是如果是多个进程都要打开自己的文件呢?
    • 系统中会存在大量的被打开的文件,OS面对如此的文件也要管理起来,通过先描述,再组织

 

  •  在内核中,OS内部会为了管理一个打开的文件,构建一个FILE结构体
  • 创建的struct file的对象,充当一个被打开的文件,如果用的很多,就用双链表组织起来,

  •  上面这张是进程和文件之间的关系
  • fd在内核本质是一个数组下标

1.7 文件的分类

  1. 磁盘文件(没有被打开)

  2. 内存文件(被进程在内存中打开)

对一个内存文件的操作流程

 1.8 理解输出重定向

每个文件描述符都是一个内核中文件描述信息数组的下标,对应有一个文件的描述信息用于操作文件,而重定向就是在不改变所操作的文件描述符的情况下,通过改变描述符对应的文件描述信息进而实现改变所操作的文件

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(1);
    // 这里的fd的分配规则是: 最小的,没有被占用的文件描述符
    // 0,1,2 -> close(1) -> fd -> 1
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    
    printf("fd: %d\n", fd); // stdout->FILE{fileno=1}->log.txt
    printf("fd: %d\n", fd);
    
    fprintf(stdout, "hello fprintf\n");
    const char* s = "hello fwrite\n";
    fwrite(s, strlen(s), 1, stdout);

   
    fflush(stdout); //stdout->_fileno == 1;
    close(fd); //后面解释
    return 0;
}

 

  • 不管是输出重定向还是输入重定向,都是在OS内部,更改fd对应的内容指向

1.8.1 更改fd->dup2

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char* argv[])
{
    if (argc != 2) {
        return 2;
    }
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    //int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    dup2(fd, 1);
    fprintf(stdout, "%s\n", argv[1]); //stdout->1->显示器
    return 0;
}

  •  dup2使old_fd变成new_fd,就相当于更改fd对应的内容指向

1.9 Linux下一切皆文件

linux是用C语言写的,而C语言要实现面向对象,甚至是运行时多态,必须封装

 但是C语言struct只能封装成员属性,要封装成员方法,就需要使用函数指针(用来调用成员方法)

  • linux的设计哲学:体现在操作系统的软件设计层面
  •  Linux看待所有硬件都是struct file,通过函数指针调来调去,这是一种统一化

2. 缓冲区 

缓冲区就是一段内存空间

  • 存在缓冲区的原因是:为了提高整机效率,提高用户的响应速度,

2.1 刷新策略

  1. 立即刷新
  2. 行刷新(行缓冲) \n
  3. 满刷新(全缓冲)

所有设备都是倾向于全缓冲的,特殊情况:a.用户强制刷新(fflush),b.进程退出

2.2 缓冲区的位置

 

 

  • 第一个程序,向显示器打印输出4行文本,
  • 向普通文件(磁盘上),打印的时候,变成了7行,其中:
    • C语言 IO接口是打印了2次的
    • 系统接口,只打印1次和向显示器打印的一样
  • 第二个程序,加上了fflush(stdout)强制刷新,向显示器和向普通文件(磁盘上)输出的文本都是4行

解释上面2个程序产生不同结果的原因

  1. 如果向显示器打印,刷新策略就是行刷新,那么最后执行fork的时候,
    一定是函数执行完了 && 数据已经被刷新了,那么fork就无意义
  2. 如果你对应的程序进行了重定向,要向磁盘文件打印
    隐形的刷新策略就变成的了全缓冲,那么\n就无意义
  3. 第2个程序中的fflush(stdout)强制刷新,不管刷新策略是什么都会被强制刷新
  4. fork()之前的函数已经执行完了,但是并不代表数据已经刷新了!!
    这些数据在当前进程对应的C标准库中的缓冲区
    这些数据也是父进程的数据,fork()创建子进程,是会发生写时拷贝的,这就是为第一个程序向普通文件(磁盘上),打印的时候,变成了7行的原因

缓冲区如果是OS统一提供的,那么我们上面的代码,表现应该是一样,所以是C标准库提供且维护的  

2.3 用户级缓冲区

  •  C标准库给我们提供的用户级缓冲区,在struct FILE 结构体中,这个结构体中不仅封装了fd
    还包含了该文件fd对应的语言层的缓冲区结构

 2.4 简单设计用户级缓冲区

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#define NUM 1024

struct MyFILE_ {
    int fd;
    char buffer[1024];
    int end; //当前缓冲区的结尾
};

typedef struct MyFILE_ MyFILE;

MyFILE* fopen_(const char* pathname, const char* mode)
{
    assert(pathname);
    assert(mode);

    MyFILE* fp = NULL;
    if (*mode == 'w') {
        // 假设是以'w'的方式打开
        int fd = open(pathname, O_WRONLY | O_TRUNC | O_CREAT, 0666);// 系统接口
        if (fd >= 0)
        {
            fp = (MyFILE*)malloc(sizeof(MyFILE));
            memset(fp, 0, sizeof(MyFILE));
            fp->fd = fd;
        }
    }
    
    return fp;
}

// C标准库中的实现!
void fputs_(const char* message, MyFILE* fp)
{
    assert(message);
    assert(fp);

    // 把数据存放在缓冲区中
    strcpy(fp->buffer + fp->end, message); //abcde\0
    fp->end += strlen(message);

    printf("%s\n", fp->buffer);

    //暂时没有刷新
    if (fp->fd == 0)
    {
        //标准输入
    }
    else if (fp->fd == 1)
    {
        //标准输出
        if (fp->buffer[fp->end - 1] == '\n')
        {
            write(fp->fd, fp->buffer, fp->end);
            fp->end = 0;
        }
    }
    else if (fp->fd == 2)
    {
        //标准错误
    }
    else
    {
        //其他文件
    }
}

void fflush_(MyFILE* fp)
{
    assert(fp);

    if (fp->end != 0)
    {
        //暂且认为刷新了--其实是把数据写到了内核
        write(fp->fd, fp->buffer, fp->end);
        syncfs(fp->fd); //将数据写入到磁盘
        fp->end = 0;
    }
}

void fclose_(MyFILE* fp)
{
    assert(fp);
    fflush_(fp);
    close(fp->fd);
    free(fp);
}

int main()
{
    //close(1);
    MyFILE* fp = fopen_("./log.txt", "w");
    if (fp == NULL)
    {
        printf("open file error");
        return 1;
    }

    fputs_("one: hello world", fp);

    fork();

    fclose_(fp);
}

2.5 对stdout和stderr的补充

 

  •  1和2对应的都是显示器文件
  • ./myfile > ok.txt 2>err.txt    将标准输出重定向到ok.txt中,标准错误重定向到err.txt
  • ./myfile > log.txt 2>&1    将标准输出标准错误都重定向到log.txt

3. 文件系统

 3.1 背景知识

  1. 文件系统中的磁盘->磁盘级文件是没有被打开的文件
  2. 学磁盘级别文件的侧重点
    1. 单个文件角度 -- 这个文件位置?,多大?,其他属性...
    2. 站在系统角度 -- 一共有多少个文件?,各自的属性在哪?,如何快速找到,..
  3.  磁盘文件
    1. 内存 - 掉电易失存储介质
    2. 磁盘 -- 永久性存储介质 -- SSD,U盘,flash卡,光盘,磁带
      磁盘不仅是一个外设,而且还是我们计算机中唯一的一个机械设备

4. 磁盘结构 

4.1 物理结构

  • 磁盘的盘面上会存储数据,里面有大量的像磁铁一样的东西,它们是有正负性
  • 先磁盘里写入,本质就是改变磁盘上的正负性,毕竟计算机只认识二进制

4.2  存储结构

  •  灰色的圆圈叫做磁道,磁道的一部分叫做扇区

4.2.1 CHS寻址方案 

在物理上,如何把数据写入到指定的扇区中?

  •  通过一中叫CHS的寻址方案,通过这种方案所有的扇区就都能够找到了
    • 1.在那一个面上(对应的就是那一个磁头)
    • 2.在那一个磁道(柱面)上
    • 3.在那一个扇区上

4.2.2 磁盘的抽象(虚拟,逻辑)结构

  • 磁盘的存储数据的基本单位是512字节 
  •  将磁盘的盘面展开,想象成一种线性结构
  • 访问一个扇区,只要通过数组的下标就可以了

4.3 对磁盘的管理

  •  将数据存储到磁盘中 -> 将数据存储到该数组
  • 找到磁盘特定扇区的位置 -> 找到数组特定的位置
  • 对磁盘的管理 - > 对数组的管理

对一个小分区(小数组) 的管理

 

  •  虽然磁盘的基本单位是扇区(512字节),但是操作系统(文件系统)和磁盘进行IO的基本单位是:4KB(8*512byte),硬件和软件(OS)进行了解耦!
  •  如果基本单位太小了,有可能会导致多次IO,进而导致效率的降低!
  • 如果操作系统使用的是和磁盘一样的大小,万一磁盘基本大小变了的话,OS的源代码也必须发生改变,所以IO没有以512字节为基本单位

4.4 存储文件

在Linux在磁盘上存储文件的时候,内容和属性是分开存储的!

  •  Date blocks:多个4KB(扇区*8)大小的集合,报错的都是特定文件的内容
  • inode Table:inode是一个大小为128字节的空间,保存的是对应文件的属性
    该块组内,所有文件的inode空间的集合,需要标识唯一性,每一个inode块,都要有一个inode编号!
    一般而言,一个文件,一个inode对应node编号
  • inode Bitmap:假设有10000+个blocks,10000+比特位:比特位和特定的indoe是一一对应的,其中比特位为1,代表该inode被占用,否则表示可用
  • Block Bitmap:假设有10000+个blocks,10000+比特位:比特位和特定的block是一一对应的,其中比特位为1,代表该block被占用,否则表示可用
  •  GDT(简写): 快组描述符,这个快组多大,已经使用了多少了,有多少个inode,已经占用了多少个,还剩多少,一共多少个block,使用了多少...
  • SuperBlock(简写):文件系统的属性信息
  • 这些分区使一个文件的信息可追溯,可管理

上面的快组分割成为上面的内容,并且写入相关的管理数据 -> 每一个快组都这么做 -> 整个分区就被写入了文件系统信息(格式化) 


  •  一个文件"只"对应一个inode属性节点,inode编号,但是一个文件不只有一个block,

 4.4.1 认识inode编号

磁盘文件由两部分构成,分别是文件内容文件属性。文件内容就是文件当中存储的数据,文件属性就是文件的一些基本信息,例如文件名、文件大小以及文件创建时间等信息都是文件属性,文件属性又被称为元信息

  • 在Linux操作系统中,文件的元信息和内容是分离存储的,其中保存元信息的结构称之为inode,因为系统当中可能存在大量的文件,所以我们需要给每个文件的属性集起一个唯一的编号,即inode号
  • 也就是说,inode是一个文件的属性集合,Linux中几乎每个文件都有一个inode,为了区分系统当中大量的inode,我们为每个inode设置了inode编号。 

如何理解创建一个空文件? 

  1. 通过遍历inode位图的方式,找到一个空闲的inode。
  2. 在inode表当中找到对应的inode,并将文件的属性信息填充进inode结构中。
  3. 将该文件的文件名和inode指针添加到目录文件的数据块中。

 如何理解对文件写入信息?

  • 通过文件的inode编号找到对应的inode结构。
  • 通过inode结构找到存储该文件内容的数据块,并将数据写入数据块。
  • 若不存在数据块或申请的数据块已被写满,则通过遍历块位图的方式找到一个空闲的块号,并在数据区当中找到对应的空闲块,再将数据写入数据块,最后还需要建立数据块和inode结构的对应关系。

如何理解删除一个文件?

  • 将该文件对应的inode在inode位图当中置为无效
  • 将该文件申请过的数据块在块位图当中置为无效

注意:  短时间是可以恢复的,但如果后续创建其他文件时或者是对其他文件进行了写入操作
          申请inode编号和数据块号,可能会将该置为无效的inode和数据块号分配出去,
          此时删除文件的数据就会被覆盖,也就无法恢复文件了

为什么拷贝文件的时候很慢,而删除文件的时候很快? 

  • 因为拷贝文件需要先创建文件,然后再对该文件进行写入操作,该过程需要先申请inode号并填入文件的属性信息,
  • 之后还需要再申请数据块号,最后才能进行文件内容的数据拷贝,
  • 而删除文件只需将对应文件的inode号和数据块号置为无效即可,无需真正的删除文件,因此拷贝文件是很慢的,而删除文件是很快的。
  • 这就像建楼一样,我们需要很长时间才能建好一栋楼,而我们若是想拆除一栋楼,只需在这栋楼上写上一个“拆”字即可。

 如何理解目录

  • 都说在Linux下一切皆文件,目录当然也可以被看作为文件。
  • 目录有自己的属性信息,目录的inode结构当中存储的就是目录的属性信息,比如目录的大小、目录的拥有者等。
  • 目录也有自己的内容,目录的数据块当中存储的就是该目录下的文件名以及对应文件的inode指针。

注意

  1. 每个文件的文件名并没有存储在自己的inode结构当中,而是存储在该文件所处目录文件的文件内容当中。
  2. 因为计算机并不关注文件的文件名,计算机只关注文件的inode号,而文件名和文件的inode指针存储在其目录文件的文件内容当中后,目录通过文件名和文件的inode指针即可将文件名和文件内容及其属性连接起来。

4.4.2 文件名 VS inode编号

  •  找到文件: inode编号(依托于目录结构) -> 分区特定的bg -> inode -> 属性 -> 内容
  • 在linux中inode属性里面,是没有文件名这样的说法的,但是目录也是文件,它也应该有自己的inode,有自己的data block,
  • 文件名 : inode 编号存在一种映射关系,互为Key值
  •  inode是固定的,datablock是固定的!

 4.4.2 在linux中删除文件,能恢复出来吗?

  • 能恢复出来,这与inode编号有关,如果inode编号,没有被使用,inode和datablock没有被重复占用,就可以恢复删除文件

4.4.3 文件的三个时间

  • Access 文件最后被访问的时间。
  • Modify 文件内容最后的修改时间。
  • Change 文件属性最后的修改时间。
  • 当我们修改文件内容时,文件的大小一般也会随之改变,所以一般情况下Modify的改变会带动Change一起改变,
  • 修改文件属性一般不会影响到文件内容,所以一般情况下Change的改变不会带动Modify的改变

我们若是想将文件的这三个时间都更新到最新状态,可以使用命令touch 文件名来进行时间更新。

注意: 当某一文件存在时使用touch命令,此时touch命令的作用变为更新文件信息

5. 软硬连接 

5.1  软连接: ln -s 源文件 目标文件

  • 软连接有独立的inode -> 它是一个独立文件
  • 特性: 软连接的文件内容,是指向的文件对应的路径!
  • 应用: 相当于windows下的快捷方式

5.2 硬链接: ln 源文件 目标文件

  • 硬连接没有独立的inode -> 它不是一个独立文件
  • 创建硬链接时,仅仅只是在指定的目录下,建立了文件名 和 指定inode映射关系

5.3 软硬链接的区别

  • 软链接是一个独立的文件,有独立的inode,而硬链接没有独立的inode
  • 软链接相当于快捷方式,硬链接本质没有创建文件,只是建立了一个文件名和已有的inode的映射关系,并写入当前目录 

5.4 inode的引用计数 

  •  这个引用计数表示有多少个文件名与它相关,
  • 当我们删除一个文件的时候,并不是把这个文件inode删除,而是将这个文件的inode引用计数--
    当引用计数为0的时候,这个文件,才是真正的删除

  •  默认创建目录:引用计数(硬链接)默认是2
    • 自己目录名:inode
    • 自己目录内部: inode

6. 动态库.so 与 静态库.a

 6.1 编写: 静态库

 

 

  •  生成一个静态库的命令:  ar -rc lib文件名.a 其他.o文件

  •  hello这个包里面有.h静态库

 6.2 使用: 静态库

  • 第一步就是要把hello这个包拷贝到当前路径,也就是间接性的把这个静态库拷贝当前路径
  • gcc 源文件 -I(大写的i) 头文件搜索路径 -L 库文件搜索路径 -l (小写的l)指定静态库

6.3 编写: 动态库

  • gcc -c -fPIC 其他.c -o 其他.o

  • gcc -shared 其他.o -o lib文件名.so

6.4 使用: 动态库 

  •  动态库是一个独立的库文件,它可以和可执行程序,分批加载!!!

  

  • 当动态库和静态库同时存在时,默认调用动态库
  • 如果这时要使用静态库,需要在最后面加上-static

上面的那段代码只是给gcc说了动态库路径,系统加载器不知道,需要给它说明动态库路径 

6.4.1 方法一:  环境变量

  •  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:要使用的动态库路径
  • 不过这种方式重启之后就会被清除

6.4.2 方法二:  新增配置文件

  •  sudo touch /etc/ld.so.conf.d/文件名.conf
  • sudo vim /etc/ld.so.conf.d/文件名.conf ,将自己写的动态库的路径放进去
  • sudo ldconfig,更新配置文件
  • 这种方式设置之后,重启依旧生效

  •  sudo rm /etc/ld.so.conf.d/文件名.conf
  • sudo ldconfig,清理缓存

6.4.3 方法三:  软连接

  •  sudo ln -s 动态库的路径 /lib64/lib目标文件名.so
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
预处理是数据分析中非常重要的步骤,它是为了准确分析数据而对数据进行的一系列操作。对于lesson数据进行预处理的目的是为了优化数据的质量、可用性和可靠性,以便更好地支持后续的数据分析工作。下面我将从数据清洗、数据规范化、数据转换、数据集成四个方面说明如何对lesson数据进行预处理。 首先是数据清洗,这是预处理的第一个步骤。数据清洗的目的是识别和处理数据中的错误、不一致、重复和缺失等问题,使得数据能够精确无误地支持后续的分析工作。对于lesson数据,我们可以通过删除重复值、填充缺失值、调整不一致的数据格式等方式进行数据清洗。 其次是数据规范化,这是为了处理数据的格式和范围,使得数据更加一致性和可比性。对于lesson数据,我们可以进行数据标准化,如将日期格式统一为YYYY-MM-DD,将文本内容转化为小写或大写等等。 然后是数据转换,这是为了将数据从原始格式转化为适合分析的格式。对于lesson数据,我们可以进行数据转换,如将非数值数据转换为数值数据,对数据进行分类或聚类,进行分箱等等。 最后是数据集成,这是为了将多个数据集合并成一个更大、更全面和更一致的数据集。对于lesson数据,我们可以将不同的数据源集成,比如将学生的成绩、出勤记录、课程评价等数据集成在一起,以进行更全面的数据分析和挖掘。 综上所述,通过数据清洗、数据规范化、数据转换、数据集成等多方面预处理操作,我们可以提高lesson数据的质量、可用性和可靠性,为后续的数据分析工作提供有力的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值